├── Android64 └── Debug │ ├── AndroidManifest.xml │ ├── OpenAIClient.dcu │ ├── OpenAIClient.o │ ├── OpenAIDtos.dcu │ ├── OpenAIDtos.o │ ├── OpenAIJson.dcu │ ├── OpenAIJson.o │ ├── OpenApiHttp.dcu │ ├── OpenApiHttp.o │ ├── OpenApiJson.dcu │ ├── OpenApiJson.o │ ├── OpenApiRest.dcu │ ├── OpenApiRest.o │ ├── OpenApiUtils.dcu │ ├── OpenApiUtils.o │ ├── UliGpt.o │ ├── classes.dex │ ├── colors.xml │ ├── splash_image_def.xml │ ├── strings.xml │ ├── styles-v21.xml │ ├── styles.xml │ ├── umain.dcu │ └── umain.o ├── README.md ├── Source ├── OpenAIClient.pas ├── OpenAIDtos.pas ├── OpenAIJson.pas ├── OpenApiFpc.pas ├── OpenApiHttp.pas ├── OpenApiJson.pas ├── OpenApiRest.pas ├── OpenApiSparkle.pas └── OpenApiUtils.pas ├── UliGpt.deployproj ├── UliGpt.dpr ├── UliGpt.dproj ├── UliGpt.dproj.local ├── UliGpt.identcache ├── UliGpt.res ├── UliGpt.skincfg ├── Win32 └── Debug │ ├── OpenAIClient.dcu │ ├── OpenAIDtos.dcu │ ├── OpenAIJson.dcu │ ├── OpenApiHttp.dcu │ ├── OpenApiJson.dcu │ ├── OpenApiRest.dcu │ ├── OpenApiUtils.dcu │ └── umain.dcu ├── umain.fmx └── umain.pas /Android64/Debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Android64/Debug/OpenAIClient.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/OpenAIClient.dcu -------------------------------------------------------------------------------- /Android64/Debug/OpenAIClient.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/OpenAIClient.o -------------------------------------------------------------------------------- /Android64/Debug/OpenAIDtos.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/OpenAIDtos.dcu -------------------------------------------------------------------------------- /Android64/Debug/OpenAIDtos.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/OpenAIDtos.o -------------------------------------------------------------------------------- /Android64/Debug/OpenAIJson.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/OpenAIJson.dcu -------------------------------------------------------------------------------- /Android64/Debug/OpenAIJson.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/OpenAIJson.o -------------------------------------------------------------------------------- /Android64/Debug/OpenApiHttp.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/OpenApiHttp.dcu -------------------------------------------------------------------------------- /Android64/Debug/OpenApiHttp.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/OpenApiHttp.o -------------------------------------------------------------------------------- /Android64/Debug/OpenApiJson.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/OpenApiJson.dcu -------------------------------------------------------------------------------- /Android64/Debug/OpenApiJson.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/OpenApiJson.o -------------------------------------------------------------------------------- /Android64/Debug/OpenApiRest.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/OpenApiRest.dcu -------------------------------------------------------------------------------- /Android64/Debug/OpenApiRest.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/OpenApiRest.o -------------------------------------------------------------------------------- /Android64/Debug/OpenApiUtils.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/OpenApiUtils.dcu -------------------------------------------------------------------------------- /Android64/Debug/OpenApiUtils.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/OpenApiUtils.o -------------------------------------------------------------------------------- /Android64/Debug/UliGpt.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/UliGpt.o -------------------------------------------------------------------------------- /Android64/Debug/classes.dex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/classes.dex -------------------------------------------------------------------------------- /Android64/Debug/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Android64/Debug/splash_image_def.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Android64/Debug/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Android64/Debug/styles-v21.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /Android64/Debug/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /Android64/Debug/umain.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/umain.dcu -------------------------------------------------------------------------------- /Android64/Debug/umain.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Android64/Debug/umain.o -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UlichatOpenAI 2 | 3 | Based on the work of https://landgraf.dev/en/ 4 | I have designed an app to test the GPT chat of OpenAI 5 | 6 | https://github.com/landgraf-dev/openai-delphi 7 | 8 | Version 1.0 9 | - This version 10 | 11 | ![WhatsApp Image 2023-01-25 at 11 01 51 AM](https://user-images.githubusercontent.com/65096480/214685756-3a499cc6-fc0d-41f9-bb5c-19ae81502870.jpeg) 12 | 13 | 14 | ## General Info 15 | >This version has labels in 3 languages, Spanish, English and Polish 16 | >but if you want you can add your native language in the function 17 | >setLanguage ( setLeng() ) 18 | -------------------------------------------------------------------------------- /Source/OpenAIClient.pas: -------------------------------------------------------------------------------- 1 | unit OpenAIClient; 2 | 3 | interface 4 | 5 | uses 6 | SysUtils, 7 | OpenApiRest, 8 | OpenAIJson, 9 | OpenAIDtos; 10 | 11 | type 12 | TRestService = class; 13 | TOpenAIService = class; 14 | TOpenAIConfig = class; 15 | TOpenAIClient = class; 16 | 17 | TRestService = class(TCustomRestService) 18 | protected 19 | function CreateConverter: TCustomJsonConverter; override; 20 | function Converter: TJsonConverter; 21 | end; 22 | 23 | /// 24 | /// The OpenAI REST API 25 | /// 26 | IOpenAIService = interface(IInvokable) 27 | ['{27B11300-4F11-4950-8B64-662C16C89F7B}'] 28 | /// 29 | /// Lists the currently available (non-finetuned) models, and provides basic information about each one such as the owner and availability. 30 | /// 31 | function ListEngines: TListEnginesResponse; 32 | /// 33 | /// Retrieves a model instance, providing basic information about it such as the owner and availability. 34 | /// 35 | /// 36 | /// The ID of the engine to use for this request 37 | /// 38 | function RetrieveEngine(EngineId: string): TEngine; 39 | /// 40 | /// Creates a completion for the provided prompt and parameters 41 | /// 42 | function CreateCompletion(Body: TCreateCompletionRequest): TCreateCompletionResponse; 43 | /// 44 | /// Creates a new edit for the provided input, instruction, and parameters 45 | /// 46 | function CreateEdit(Body: TCreateEditRequest): TCreateEditResponse; 47 | /// 48 | /// Creates an image given a prompt. 49 | /// 50 | function CreateImage(Body: TCreateImageRequest): TImagesResponse; 51 | /// 52 | /// Creates an embedding vector representing the input text. 53 | /// 54 | function CreateEmbedding(Body: TCreateEmbeddingRequest): TCreateEmbeddingResponse; 55 | /// 56 | /// The search endpoint computes similarity scores between provided query and documents. Documents can be passed directly to the API if there are no more than 200 of them. 57 | /// 58 | /// To go beyond the 200 document limit, documents can be processed offline and then used for efficient retrieval at query time. When `file` is set, the search endpoint searches over all the documents in the given file and returns up to the `max_rerank` number of documents. These documents will be returned along with their search scores. 59 | /// 60 | /// The similarity score is a positive score that usually ranges from 0 to 300 (but can sometimes go higher), where a score above 200 usually means the document is semantically similar to the query. 61 | /// 62 | /// 63 | /// The ID of the engine to use for this request. You can select one of `ada`, `babbage`, `curie`, or `davinci`. 64 | /// 65 | function CreateSearch(EngineId: string; Body: TCreateSearchRequest): TCreateSearchResponse; 66 | /// 67 | /// Returns a list of files that belong to the user's organization. 68 | /// 69 | function ListFiles: TListFilesResponse; 70 | /// 71 | /// Returns information about a specific file. 72 | /// 73 | /// 74 | /// The ID of the file to use for this request 75 | /// 76 | function RetrieveFile(FileId: string): TOpenAIFile; 77 | /// 78 | /// Delete a file. 79 | /// 80 | /// 81 | /// The ID of the file to use for this request 82 | /// 83 | function DeleteFile(FileId: string): TDeleteFileResponse; 84 | /// 85 | /// Returns the contents of the specified file 86 | /// 87 | /// 88 | /// The ID of the file to use for this request 89 | /// 90 | function DownloadFile(FileId: string): string; 91 | /// 92 | /// Answers the specified question using the provided documents and examples. 93 | /// 94 | /// The endpoint first [searches](/docs/api-reference/searches) over provided documents or files to find relevant context. The relevant context is combined with the provided examples and question to create the prompt for [completion](/docs/api-reference/completions). 95 | /// 96 | function CreateAnswer(Body: TCreateAnswerRequest): TCreateAnswerResponse; 97 | /// 98 | /// Classifies the specified `query` using provided examples. 99 | /// 100 | /// The endpoint first [searches](/docs/api-reference/searches) over the labeled examples 101 | /// to select the ones most relevant for the particular query. Then, the relevant examples 102 | /// are combined with the query to construct a prompt to produce the final label via the 103 | /// [completions](/docs/api-reference/completions) endpoint. 104 | /// 105 | /// Labeled examples can be provided via an uploaded `file`, or explicitly listed in the 106 | /// request using the `examples` parameter for quick tests and small scale use cases. 107 | /// 108 | function CreateClassification(Body: TCreateClassificationRequest): TCreateClassificationResponse; 109 | /// 110 | /// List your organization's fine-tuning jobs 111 | /// 112 | function ListFineTunes: TListFineTunesResponse; 113 | /// 114 | /// Creates a job that fine-tunes a specified model from a given dataset. 115 | /// 116 | /// Response includes details of the enqueued job including job status and the name of the fine-tuned models once complete. 117 | /// 118 | /// [Learn more about Fine-tuning](/docs/guides/fine-tuning) 119 | /// 120 | function CreateFineTune(Body: TCreateFineTuneRequest): TFineTune; 121 | /// 122 | /// Gets info about the fine-tune job. 123 | /// 124 | /// [Learn more about Fine-tuning](/docs/guides/fine-tuning) 125 | /// 126 | /// 127 | /// The ID of the fine-tune job 128 | /// 129 | function RetrieveFineTune(FineTuneId: string): TFineTune; 130 | /// 131 | /// Immediately cancel a fine-tune job. 132 | /// 133 | /// 134 | /// The ID of the fine-tune job to cancel 135 | /// 136 | function CancelFineTune(FineTuneId: string): TFineTune; 137 | /// 138 | /// Get fine-grained status updates for a fine-tune job. 139 | /// 140 | /// 141 | /// The ID of the fine-tune job to get events for. 142 | /// 143 | /// 144 | /// Whether to stream events for the fine-tune job. If set to true, 145 | /// events will be sent as data-only 146 | /// [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) 147 | /// as they become available. The stream will terminate with a 148 | /// `data: [DONE]` message when the job is finished (succeeded, cancelled, 149 | /// or failed). 150 | /// 151 | /// If set to false, only events generated so far will be returned. 152 | /// 153 | function ListFineTuneEvents(FineTuneId: string; Stream: Boolean): TListFineTuneEventsResponse; 154 | /// 155 | /// Lists the currently available models, and provides basic information about each one such as the owner and availability. 156 | /// 157 | function ListModels: TListModelsResponse; 158 | /// 159 | /// Retrieves a model instance, providing basic information about the model such as the owner and permissioning. 160 | /// 161 | /// 162 | /// The ID of the model to use for this request 163 | /// 164 | function RetrieveModel(Model: string): TModel; 165 | /// 166 | /// Delete a fine-tuned model. You must have the Owner role in your organization. 167 | /// 168 | /// 169 | /// The model to delete 170 | /// 171 | function DeleteModel(Model: string): TDeleteModelResponse; 172 | /// 173 | /// Classifies if text violates OpenAI's Content Policy 174 | /// 175 | function CreateModeration(Body: TCreateModerationRequest): TCreateModerationResponse; 176 | end; 177 | 178 | TOpenAIService = class(TRestService, IOpenAIService) 179 | public 180 | function ListEngines: TListEnginesResponse; 181 | /// 182 | /// The ID of the engine to use for this request 183 | /// 184 | function RetrieveEngine(EngineId: string): TEngine; 185 | function CreateCompletion(Body: TCreateCompletionRequest): TCreateCompletionResponse; 186 | function CreateEdit(Body: TCreateEditRequest): TCreateEditResponse; 187 | function CreateImage(Body: TCreateImageRequest): TImagesResponse; 188 | function CreateEmbedding(Body: TCreateEmbeddingRequest): TCreateEmbeddingResponse; 189 | /// 190 | /// The ID of the engine to use for this request. You can select one of `ada`, `babbage`, `curie`, or `davinci`. 191 | /// 192 | function CreateSearch(EngineId: string; Body: TCreateSearchRequest): TCreateSearchResponse; 193 | function ListFiles: TListFilesResponse; 194 | /// 195 | /// The ID of the file to use for this request 196 | /// 197 | function RetrieveFile(FileId: string): TOpenAIFile; 198 | /// 199 | /// The ID of the file to use for this request 200 | /// 201 | function DeleteFile(FileId: string): TDeleteFileResponse; 202 | /// 203 | /// The ID of the file to use for this request 204 | /// 205 | function DownloadFile(FileId: string): string; 206 | function CreateAnswer(Body: TCreateAnswerRequest): TCreateAnswerResponse; 207 | function CreateClassification(Body: TCreateClassificationRequest): TCreateClassificationResponse; 208 | function ListFineTunes: TListFineTunesResponse; 209 | function CreateFineTune(Body: TCreateFineTuneRequest): TFineTune; 210 | /// 211 | /// The ID of the fine-tune job 212 | /// 213 | function RetrieveFineTune(FineTuneId: string): TFineTune; 214 | /// 215 | /// The ID of the fine-tune job to cancel 216 | /// 217 | function CancelFineTune(FineTuneId: string): TFineTune; 218 | /// 219 | /// The ID of the fine-tune job to get events for. 220 | /// 221 | /// 222 | /// Whether to stream events for the fine-tune job. If set to true, 223 | /// events will be sent as data-only 224 | /// [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) 225 | /// as they become available. The stream will terminate with a 226 | /// `data: [DONE]` message when the job is finished (succeeded, cancelled, 227 | /// or failed). 228 | /// 229 | /// If set to false, only events generated so far will be returned. 230 | /// 231 | function ListFineTuneEvents(FineTuneId: string; Stream: Boolean): TListFineTuneEventsResponse; 232 | function ListModels: TListModelsResponse; 233 | /// 234 | /// The ID of the model to use for this request 235 | /// 236 | function RetrieveModel(Model: string): TModel; 237 | /// 238 | /// The model to delete 239 | /// 240 | function DeleteModel(Model: string): TDeleteModelResponse; 241 | function CreateModeration(Body: TCreateModerationRequest): TCreateModerationResponse; 242 | end; 243 | 244 | TOpenAIConfig = class(TCustomRestConfig) 245 | public 246 | constructor Create; 247 | end; 248 | 249 | IOpenAIClient = interface(IRestClient) 250 | /// 251 | /// The OpenAI REST API 252 | /// 253 | function OpenAI: IOpenAIService; 254 | end; 255 | 256 | TOpenAIClient = class(TCustomRestClient, IOpenAIClient) 257 | public 258 | function OpenAI: IOpenAIService; 259 | constructor Create; 260 | end; 261 | 262 | implementation 263 | 264 | { TRestService } 265 | 266 | function TRestService.CreateConverter: TCustomJsonConverter; 267 | begin 268 | Result := TJsonConverter.Create; 269 | end; 270 | 271 | function TRestService.Converter: TJsonConverter; 272 | begin 273 | Result := TJsonConverter(inherited Converter); 274 | end; 275 | 276 | { TOpenAIService } 277 | 278 | function TOpenAIService.ListEngines: TListEnginesResponse; 279 | var 280 | Request: IRestRequest; 281 | Response: IRestResponse; 282 | begin 283 | Request := CreateRequest('/engines', 'GET'); 284 | Request.AddHeader('Accept', 'application/json'); 285 | Response := Request.Execute; 286 | CheckError(Response); 287 | Result := Converter.TListEnginesResponseFromJson(Response.ContentAsString); 288 | end; 289 | 290 | function TOpenAIService.RetrieveEngine(EngineId: string): TEngine; 291 | var 292 | Request: IRestRequest; 293 | Response: IRestResponse; 294 | begin 295 | Request := CreateRequest('/engines/{engine_id}', 'GET'); 296 | Request.AddUrlParam('engine_id', EngineId); 297 | Request.AddHeader('Accept', 'application/json'); 298 | Response := Request.Execute; 299 | CheckError(Response); 300 | Result := Converter.TEngineFromJson(Response.ContentAsString); 301 | end; 302 | 303 | function TOpenAIService.CreateCompletion(Body: TCreateCompletionRequest): TCreateCompletionResponse; 304 | var 305 | Request: IRestRequest; 306 | Response: IRestResponse; 307 | begin 308 | Request := CreateRequest('/completions', 'POST'); 309 | Request.AddBody(Converter.TCreateCompletionRequestToJson(Body)); 310 | Request.AddHeader('Content-Type', 'application/json'); 311 | Request.AddHeader('Accept', 'application/json'); 312 | Response := Request.Execute; 313 | CheckError(Response); 314 | Result := Converter.TCreateCompletionResponseFromJson(Response.ContentAsString); 315 | end; 316 | 317 | function TOpenAIService.CreateEdit(Body: TCreateEditRequest): TCreateEditResponse; 318 | var 319 | Request: IRestRequest; 320 | Response: IRestResponse; 321 | begin 322 | Request := CreateRequest('/edits', 'POST'); 323 | Request.AddBody(Converter.TCreateEditRequestToJson(Body)); 324 | Request.AddHeader('Content-Type', 'application/json'); 325 | Request.AddHeader('Accept', 'application/json'); 326 | Response := Request.Execute; 327 | CheckError(Response); 328 | Result := Converter.TCreateEditResponseFromJson(Response.ContentAsString); 329 | end; 330 | 331 | function TOpenAIService.CreateImage(Body: TCreateImageRequest): TImagesResponse; 332 | var 333 | Request: IRestRequest; 334 | Response: IRestResponse; 335 | begin 336 | Request := CreateRequest('/images/generations', 'POST'); 337 | Request.AddBody(Converter.TCreateImageRequestToJson(Body)); 338 | Request.AddHeader('Content-Type', 'application/json'); 339 | Request.AddHeader('Accept', 'application/json'); 340 | Response := Request.Execute; 341 | CheckError(Response); 342 | Result := Converter.TImagesResponseFromJson(Response.ContentAsString); 343 | end; 344 | 345 | function TOpenAIService.CreateEmbedding(Body: TCreateEmbeddingRequest): TCreateEmbeddingResponse; 346 | var 347 | Request: IRestRequest; 348 | Response: IRestResponse; 349 | begin 350 | Request := CreateRequest('/embeddings', 'POST'); 351 | Request.AddBody(Converter.TCreateEmbeddingRequestToJson(Body)); 352 | Request.AddHeader('Content-Type', 'application/json'); 353 | Request.AddHeader('Accept', 'application/json'); 354 | Response := Request.Execute; 355 | CheckError(Response); 356 | Result := Converter.TCreateEmbeddingResponseFromJson(Response.ContentAsString); 357 | end; 358 | 359 | function TOpenAIService.CreateSearch(EngineId: string; Body: TCreateSearchRequest): TCreateSearchResponse; 360 | var 361 | Request: IRestRequest; 362 | Response: IRestResponse; 363 | begin 364 | Request := CreateRequest('/engines/{engine_id}/search', 'POST'); 365 | Request.AddUrlParam('engine_id', EngineId); 366 | Request.AddBody(Converter.TCreateSearchRequestToJson(Body)); 367 | Request.AddHeader('Content-Type', 'application/json'); 368 | Request.AddHeader('Accept', 'application/json'); 369 | Response := Request.Execute; 370 | CheckError(Response); 371 | Result := Converter.TCreateSearchResponseFromJson(Response.ContentAsString); 372 | end; 373 | 374 | function TOpenAIService.ListFiles: TListFilesResponse; 375 | var 376 | Request: IRestRequest; 377 | Response: IRestResponse; 378 | begin 379 | Request := CreateRequest('/files', 'GET'); 380 | Request.AddHeader('Accept', 'application/json'); 381 | Response := Request.Execute; 382 | CheckError(Response); 383 | Result := Converter.TListFilesResponseFromJson(Response.ContentAsString); 384 | end; 385 | 386 | function TOpenAIService.RetrieveFile(FileId: string): TOpenAIFile; 387 | var 388 | Request: IRestRequest; 389 | Response: IRestResponse; 390 | begin 391 | Request := CreateRequest('/files/{file_id}', 'GET'); 392 | Request.AddUrlParam('file_id', FileId); 393 | Request.AddHeader('Accept', 'application/json'); 394 | Response := Request.Execute; 395 | CheckError(Response); 396 | Result := Converter.TOpenAIFileFromJson(Response.ContentAsString); 397 | end; 398 | 399 | function TOpenAIService.DeleteFile(FileId: string): TDeleteFileResponse; 400 | var 401 | Request: IRestRequest; 402 | Response: IRestResponse; 403 | begin 404 | Request := CreateRequest('/files/{file_id}', 'DELETE'); 405 | Request.AddUrlParam('file_id', FileId); 406 | Request.AddHeader('Accept', 'application/json'); 407 | Response := Request.Execute; 408 | CheckError(Response); 409 | Result := Converter.TDeleteFileResponseFromJson(Response.ContentAsString); 410 | end; 411 | 412 | function TOpenAIService.DownloadFile(FileId: string): string; 413 | var 414 | Request: IRestRequest; 415 | Response: IRestResponse; 416 | begin 417 | Request := CreateRequest('/files/{file_id}/content', 'GET'); 418 | Request.AddUrlParam('file_id', FileId); 419 | Request.AddHeader('Accept', 'application/json'); 420 | Response := Request.Execute; 421 | CheckError(Response); 422 | Result := Converter.stringFromJson(Response.ContentAsString); 423 | end; 424 | 425 | function TOpenAIService.CreateAnswer(Body: TCreateAnswerRequest): TCreateAnswerResponse; 426 | var 427 | Request: IRestRequest; 428 | Response: IRestResponse; 429 | begin 430 | Request := CreateRequest('/answers', 'POST'); 431 | Request.AddBody(Converter.TCreateAnswerRequestToJson(Body)); 432 | Request.AddHeader('Content-Type', 'application/json'); 433 | Request.AddHeader('Accept', 'application/json'); 434 | Response := Request.Execute; 435 | CheckError(Response); 436 | Result := Converter.TCreateAnswerResponseFromJson(Response.ContentAsString); 437 | end; 438 | 439 | function TOpenAIService.CreateClassification(Body: TCreateClassificationRequest): TCreateClassificationResponse; 440 | var 441 | Request: IRestRequest; 442 | Response: IRestResponse; 443 | begin 444 | Request := CreateRequest('/classifications', 'POST'); 445 | Request.AddBody(Converter.TCreateClassificationRequestToJson(Body)); 446 | Request.AddHeader('Content-Type', 'application/json'); 447 | Request.AddHeader('Accept', 'application/json'); 448 | Response := Request.Execute; 449 | CheckError(Response); 450 | Result := Converter.TCreateClassificationResponseFromJson(Response.ContentAsString); 451 | end; 452 | 453 | function TOpenAIService.ListFineTunes: TListFineTunesResponse; 454 | var 455 | Request: IRestRequest; 456 | Response: IRestResponse; 457 | begin 458 | Request := CreateRequest('/fine-tunes', 'GET'); 459 | Request.AddHeader('Accept', 'application/json'); 460 | Response := Request.Execute; 461 | CheckError(Response); 462 | Result := Converter.TListFineTunesResponseFromJson(Response.ContentAsString); 463 | end; 464 | 465 | function TOpenAIService.CreateFineTune(Body: TCreateFineTuneRequest): TFineTune; 466 | var 467 | Request: IRestRequest; 468 | Response: IRestResponse; 469 | begin 470 | Request := CreateRequest('/fine-tunes', 'POST'); 471 | Request.AddBody(Converter.TCreateFineTuneRequestToJson(Body)); 472 | Request.AddHeader('Content-Type', 'application/json'); 473 | Request.AddHeader('Accept', 'application/json'); 474 | Response := Request.Execute; 475 | CheckError(Response); 476 | Result := Converter.TFineTuneFromJson(Response.ContentAsString); 477 | end; 478 | 479 | function TOpenAIService.RetrieveFineTune(FineTuneId: string): TFineTune; 480 | var 481 | Request: IRestRequest; 482 | Response: IRestResponse; 483 | begin 484 | Request := CreateRequest('/fine-tunes/{fine_tune_id}', 'GET'); 485 | Request.AddUrlParam('fine_tune_id', FineTuneId); 486 | Request.AddHeader('Accept', 'application/json'); 487 | Response := Request.Execute; 488 | CheckError(Response); 489 | Result := Converter.TFineTuneFromJson(Response.ContentAsString); 490 | end; 491 | 492 | function TOpenAIService.CancelFineTune(FineTuneId: string): TFineTune; 493 | var 494 | Request: IRestRequest; 495 | Response: IRestResponse; 496 | begin 497 | Request := CreateRequest('/fine-tunes/{fine_tune_id}/cancel', 'POST'); 498 | Request.AddUrlParam('fine_tune_id', FineTuneId); 499 | Request.AddHeader('Accept', 'application/json'); 500 | Response := Request.Execute; 501 | CheckError(Response); 502 | Result := Converter.TFineTuneFromJson(Response.ContentAsString); 503 | end; 504 | 505 | function TOpenAIService.ListFineTuneEvents(FineTuneId: string; Stream: Boolean): TListFineTuneEventsResponse; 506 | var 507 | Request: IRestRequest; 508 | Response: IRestResponse; 509 | begin 510 | Request := CreateRequest('/fine-tunes/{fine_tune_id}/events', 'GET'); 511 | Request.AddUrlParam('fine_tune_id', FineTuneId); 512 | Request.AddQueryParam('stream', FineTuneId); 513 | Request.AddHeader('Accept', 'application/json'); 514 | Response := Request.Execute; 515 | CheckError(Response); 516 | Result := Converter.TListFineTuneEventsResponseFromJson(Response.ContentAsString); 517 | end; 518 | 519 | function TOpenAIService.ListModels: TListModelsResponse; 520 | var 521 | Request: IRestRequest; 522 | Response: IRestResponse; 523 | begin 524 | Request := CreateRequest('/models', 'GET'); 525 | Request.AddHeader('Accept', 'application/json'); 526 | Response := Request.Execute; 527 | CheckError(Response); 528 | Result := Converter.TListModelsResponseFromJson(Response.ContentAsString); 529 | end; 530 | 531 | function TOpenAIService.RetrieveModel(Model: string): TModel; 532 | var 533 | Request: IRestRequest; 534 | Response: IRestResponse; 535 | begin 536 | Request := CreateRequest('/models/{model}', 'GET'); 537 | Request.AddUrlParam('model', Model); 538 | Request.AddHeader('Accept', 'application/json'); 539 | Response := Request.Execute; 540 | CheckError(Response); 541 | Result := Converter.TModelFromJson(Response.ContentAsString); 542 | end; 543 | 544 | function TOpenAIService.DeleteModel(Model: string): TDeleteModelResponse; 545 | var 546 | Request: IRestRequest; 547 | Response: IRestResponse; 548 | begin 549 | Request := CreateRequest('/models/{model}', 'DELETE'); 550 | Request.AddUrlParam('model', Model); 551 | Request.AddHeader('Accept', 'application/json'); 552 | Response := Request.Execute; 553 | CheckError(Response); 554 | Result := Converter.TDeleteModelResponseFromJson(Response.ContentAsString); 555 | end; 556 | 557 | function TOpenAIService.CreateModeration(Body: TCreateModerationRequest): TCreateModerationResponse; 558 | var 559 | Request: IRestRequest; 560 | Response: IRestResponse; 561 | begin 562 | Request := CreateRequest('/moderations', 'POST'); 563 | Request.AddBody(Converter.TCreateModerationRequestToJson(Body)); 564 | Request.AddHeader('Content-Type', 'application/json'); 565 | Request.AddHeader('Accept', 'application/json'); 566 | Response := Request.Execute; 567 | CheckError(Response); 568 | Result := Converter.TCreateModerationResponseFromJson(Response.ContentAsString); 569 | end; 570 | 571 | { TOpenAIConfig } 572 | 573 | constructor TOpenAIConfig.Create; 574 | begin 575 | inherited Create; 576 | BaseUrl := 'https://api.openai.com/v1/'; 577 | end; 578 | 579 | { TOpenAIClient } 580 | 581 | function TOpenAIClient.OpenAI: IOpenAIService; 582 | begin 583 | Result := TOpenAIService.Create(Config); 584 | end; 585 | 586 | constructor TOpenAIClient.Create; 587 | begin 588 | inherited Create(TOpenAIConfig.Create); 589 | end; 590 | 591 | end. 592 | -------------------------------------------------------------------------------- /Source/OpenApiFpc.pas: -------------------------------------------------------------------------------- 1 | unit OpenApiFpc; 2 | 3 | {$IFDEF FPC}{$MODE Delphi}{$ENDIF} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils, OpenApiRest, fphttpclient, opensslsockets; 9 | 10 | type 11 | THttpRestRequest = class(TRestRequest) 12 | public 13 | function Execute: IRestResponse; override; 14 | end; 15 | 16 | THttpRestResponse = class(TInterfacedObject, IRestResponse) 17 | strict private 18 | FClient: TFPHTTPClient; 19 | FContent: TStringStream; 20 | public 21 | constructor Create(Client: TFPHttpClient; Content: TStringStream); 22 | destructor Destroy; override; 23 | function StatusCode: Integer; 24 | function ContentAsString: string; 25 | function ContentAsBytes: TBytes; 26 | function GetHeader(const Name: string): string; 27 | end; 28 | 29 | THttpRestRequestFactory = class(TInterfacedObject, IRestRequestFactory) 30 | public 31 | function CreateRequest: IRestRequest; 32 | end; 33 | 34 | implementation 35 | 36 | { THttpRestRequestFactory } 37 | 38 | function THttpRestRequestFactory.CreateRequest: IRestRequest; 39 | begin 40 | Result := THttpRestRequest.Create; 41 | end; 42 | 43 | { THttpRestRequest } 44 | 45 | function THttpRestRequest.Execute: IRestResponse; 46 | var 47 | Client: TFPHTTPClient; 48 | SourceStream: TStream; 49 | Content: TStringStream; 50 | I: Integer; 51 | begin 52 | Client := TFPHTTPClient.Create(nil); 53 | try 54 | if Body <> '' then 55 | SourceStream := TStringStream.Create(Body, TEncoding.UTF8, False) 56 | else 57 | SourceStream := nil; 58 | try 59 | for I := 0 to Headers.Count - 1 do 60 | Client.AddHeader(Headers.Names[I], Headers.ValueFromIndex[I]); 61 | Client.RequestBody := SourceStream; 62 | Content := TStringStream.Create('', TEncoding.UTF8, False); 63 | try 64 | Client.HTTPMethod(Self.Method, BuildUrl, Content, []); 65 | Result := THttpRestResponse.Create(Client, Content); 66 | Content := nil; 67 | Client := nil; 68 | finally 69 | Content.Free; 70 | end; 71 | finally 72 | SourceStream.Free; 73 | end; 74 | finally 75 | Client.Free; 76 | end; 77 | end; 78 | 79 | { THttpRestResponse } 80 | 81 | function THttpRestResponse.ContentAsBytes: TBytes; 82 | begin 83 | Result := Copy(FContent.Bytes, 0, FContent.Size); 84 | end; 85 | 86 | function THttpRestResponse.ContentAsString: string; 87 | begin 88 | Result := FContent.DataString; 89 | end; 90 | 91 | constructor THttpRestResponse.Create(Client: TFPHTTPClient; Content: TStringStream); 92 | begin 93 | inherited Create; 94 | FClient := Client; 95 | FContent := Content; 96 | end; 97 | 98 | destructor THttpRestResponse.Destroy; 99 | begin 100 | FClient.Free; 101 | FContent.Free; 102 | inherited; 103 | end; 104 | 105 | function THttpRestResponse.GetHeader(const Name: string): string; 106 | begin 107 | Result := Trim(FClient.ResponseHeaders.Values[Name]); 108 | end; 109 | 110 | function THttpRestResponse.StatusCode: Integer; 111 | begin 112 | Result := FClient.ResponseStatusCode; 113 | end; 114 | 115 | initialization 116 | DefaultRequestFactory := THttpRestRequestFactory.Create;; 117 | end. 118 | -------------------------------------------------------------------------------- /Source/OpenApiHttp.pas: -------------------------------------------------------------------------------- 1 | unit OpenApiHttp; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Classes, System.Net.HttpClient, System.Net.URLClient, 7 | OpenApiRest; 8 | 9 | type 10 | THttpRestRequest = class(TRestRequest) 11 | public 12 | function Execute: IRestResponse; override; 13 | end; 14 | 15 | THttpRestResponse = class(TInterfacedObject, IRestResponse) 16 | strict private 17 | FClient: THttpClient; 18 | FResponse: IHttpResponse; 19 | FContent: TBytesStream; 20 | public 21 | constructor Create(Response: IHttpResponse; Content: TBytesStream; Client: THttpClient); 22 | destructor Destroy; override; 23 | function StatusCode: Integer; 24 | function ContentAsString: string; 25 | function ContentAsBytes: TBytes; 26 | function GetHeader(const Name: string): string; 27 | end; 28 | 29 | THttpRestRequestFactory = class(TInterfacedObject, IRestRequestFactory) 30 | public 31 | function CreateRequest: IRestRequest; 32 | end; 33 | 34 | implementation 35 | 36 | { THttpRestRequestFactory } 37 | 38 | function THttpRestRequestFactory.CreateRequest: IRestRequest; 39 | begin 40 | Result := THttpRestRequest.Create; 41 | end; 42 | 43 | { THttpRestRequest } 44 | 45 | function THttpRestRequest.Execute: IRestResponse; 46 | var 47 | Request: IHttpRequest; 48 | Response: IHttpResponse; 49 | Client: THttpClient; 50 | SourceStream: TStream; 51 | Content: TBytesStream; 52 | I: Integer; 53 | begin 54 | Client := THttpClient.Create; 55 | try 56 | Request := Client.GetRequest(Self.Method, BuildUrl); 57 | if Body <> '' then 58 | SourceStream := TStringStream.Create(Body, TEncoding.UTF8, False) 59 | else 60 | SourceStream := nil; 61 | for I := 0 to Headers.Count - 1 do 62 | Request.SetHeaderValue(Headers.Names[I], Headers.ValueFromIndex[I]); 63 | 64 | try 65 | Request.SourceStream := SourceStream; 66 | Content := TBytesStream.Create; 67 | try 68 | Response := Client.Execute(Request, Content); 69 | Result := THttpRestResponse.Create(Response, Content, Client); 70 | Content := nil; 71 | Client := nil; 72 | finally 73 | Content.Free; 74 | end; 75 | finally 76 | SourceStream.Free; 77 | end; 78 | finally 79 | Client.Free; 80 | end; 81 | end; 82 | 83 | { THttpRestResponse } 84 | 85 | function THttpRestResponse.ContentAsBytes: TBytes; 86 | begin 87 | Result := Copy(FContent.Bytes, 0, FContent.Size); 88 | end; 89 | 90 | function THttpRestResponse.ContentAsString: string; 91 | begin 92 | Result := FResponse.ContentAsString; 93 | end; 94 | 95 | constructor THttpRestResponse.Create(Response: IHttpResponse; Content: TBytesStream; Client: THttpClient); 96 | begin 97 | inherited Create; 98 | FResponse := Response; 99 | FClient := Client; 100 | FContent := Content; 101 | end; 102 | 103 | destructor THttpRestResponse.Destroy; 104 | begin 105 | FClient.Free; 106 | FContent.Free; 107 | inherited; 108 | end; 109 | 110 | function THttpRestResponse.GetHeader(const Name: string): string; 111 | begin 112 | Result := FResponse.HeaderValue[Name]; 113 | end; 114 | 115 | function THttpRestResponse.StatusCode: Integer; 116 | begin 117 | Result := FResponse.StatusCode; 118 | end; 119 | 120 | initialization 121 | DefaultRequestFactory := THttpRestRequestFactory.Create;; 122 | end. 123 | -------------------------------------------------------------------------------- /Source/OpenApiJson.pas: -------------------------------------------------------------------------------- 1 | unit OpenApiJson; 2 | 3 | {$IFDEF FPC}{$MODE Delphi}{$ENDIF} 4 | 5 | interface 6 | 7 | uses 8 | {$IFDEF FPC} 9 | fpjson, jsonparser, 10 | {$ELSE} 11 | Generics.Collections, 12 | JSON, 13 | {$ENDIF} 14 | SysUtils; 15 | 16 | type 17 | {$IFDEF FPC} 18 | TJSONValue = fpjson.TJSONData; 19 | TJSONBool = fpjson.TJSONBoolean; 20 | {$ELSE} 21 | TJSONValue = System.JSON.TJSONValue; 22 | {$ENDIF} 23 | 24 | TJsonWrapper = class 25 | private 26 | function IsFloatingPoint(const Value: string): Boolean; 27 | public 28 | // method to convert basic types to/from TJSONValue 29 | function StringToJsonValue(const Value: string): TJSONValue; virtual; 30 | function StringFromJsonValue(Value: TJSONValue): string; virtual; 31 | function IntegerToJsonValue(const Value: Integer): TJSONValue; virtual; 32 | function IntegerFromJsonValue(Value: TJSONValue): Integer; virtual; 33 | function Int64ToJsonValue(const Value: Int64): TJSONValue; virtual; 34 | function Int64FromJsonValue(Value: TJSONValue): Int64; virtual; 35 | function DoubleToJsonValue(const Value: Double): TJSONValue; virtual; 36 | function DoubleFromJsonValue(Value: TJSONValue): Double; virtual; 37 | function BooleanToJsonValue(const Value: Boolean): TJSONValue; virtual; 38 | function BooleanFromJsonValue(Value: TJSONValue): Boolean; virtual; 39 | 40 | // methods for JSON object manipulation 41 | procedure ObjAddProp(JObj: TJSONValue; const Name: string; Value: TJSONValue); virtual; 42 | function ObjContains(JObj: TJSONValue; const Name: string; out Value: TJSONValue): Boolean; virtual; 43 | 44 | // methods for JSON array manipulation 45 | procedure ArrayAdd(JArr: TJSONValue; Value: TJSONValue); virtual; 46 | function ArrayGet(JArr: TJSONValue; Index: Integer): TJSONValue; virtual; 47 | function ArrayLength(JArr: TJSONValue): Integer; virtual; 48 | 49 | // JSON value constructors 50 | function CreateObject: TJSONValue; virtual; 51 | function CreateArray: TJSONValue; virtual; 52 | function CreateNull: TJSONValue; virtual; 53 | 54 | // Check for JSON types 55 | function IsObject(Value: TJSONValue): Boolean; virtual; 56 | function IsArray(Value: TJSONValue): Boolean; virtual; 57 | function IsString(Value: TJSONValue): Boolean; virtual; 58 | function IsNumber(Value: TJSONValue): Boolean; virtual; 59 | function IsBoolean(Value: TJSONValue): Boolean; virtual; 60 | function IsNull(Value: TJSONValue): Boolean; virtual; 61 | 62 | // Json generating and parsing 63 | function JsonValueToJson(Value: TJSONValue): string; virtual; 64 | function JsonToJsonValue(const Value: string): TJSONValue; virtual; 65 | end; 66 | 67 | TCustomJsonConverter = class 68 | private 69 | FJson: TJsonWrapper; 70 | protected 71 | function JsonValueToJson(Value: TJSONValue): string; 72 | function JsonToJsonValue(const Value: string): TJSONValue; 73 | property Json: TJsonWrapper read FJson; 74 | public 75 | constructor Create; overload; 76 | constructor Create(AJson: TJsonWrapper); overload; 77 | 78 | // method to convert basic types to/from TJSONValue 79 | function StringToJsonValue(const Value: string): TJSONValue; virtual; 80 | function StringFromJsonValue(Value: TJSONValue): string; virtual; 81 | function IntegerToJsonValue(const Value: Integer): TJSONValue; virtual; 82 | function IntegerFromJsonValue(Value: TJSONValue): Integer; virtual; 83 | function Int64ToJsonValue(const Value: Int64): TJSONValue; virtual; 84 | function Int64FromJsonValue(Value: TJSONValue): Int64; virtual; 85 | function DoubleToJsonValue(const Value: Double): TJSONValue; virtual; 86 | function DoubleFromJsonValue(Value: TJSONValue): Double; virtual; 87 | function BooleanToJsonValue(const Value: Boolean): TJSONValue; virtual; 88 | function BooleanFromJsonValue(Value: TJSONValue): Boolean; virtual; 89 | function TDateTimeToJsonValue(const Value: TDateTime): TJSONValue; virtual; 90 | function TDateTimeFromJsonValue(Value: TJSONValue): TDateTime; virtual; 91 | function TDateToJsonValue(const Value: TDate): TJSONValue; virtual; 92 | function TDateFromJsonValue(Value: TJSONValue): TDate; virtual; 93 | function TBytesToJsonValue(const Value: TBytes): TJSONValue; virtual; 94 | function TBytesFromJsonValue(Value: TJSONValue): TBytes; virtual; 95 | 96 | // method to convert basic types to/from JSON 97 | function StringToJson(const Source: string): string; virtual; 98 | function StringFromJson(Source: string): string; virtual; 99 | function IntegerToJson(const Source: Integer): string; virtual; 100 | function IntegerFromJson(Source: string): Integer; virtual; 101 | function Int64ToJson(const Source: Int64): string; virtual; 102 | function Int64FromJson(Source: string): Int64; virtual; 103 | function DoubleToJson(const Source: Double): string; virtual; 104 | function DoubleFromJson(const Source: string): Double; virtual; 105 | function BooleanToJson(const Source: Boolean): string; virtual; 106 | function BooleanFromJson(Source: string): Boolean; virtual; 107 | function TDateTimeToJson(const Source: TDateTime): string; virtual; 108 | function TDateTimeFromJson(Source: string): TDateTime; virtual; 109 | function TDateToJson(const Source: TDate): string; virtual; 110 | function TDateFromJson(Source: string): TDate; virtual; 111 | function TBytesToJson(const Source: TBytes): string; virtual; 112 | function TBytesFromJson(Source: string): TBytes; virtual; 113 | end; 114 | 115 | function JsonWrapper: TJsonWrapper; 116 | 117 | implementation 118 | 119 | uses 120 | OpenApiUtils; 121 | 122 | var 123 | _Json: TJsonWrapper; 124 | 125 | function JsonWrapper: TJsonWrapper; 126 | begin 127 | Result := _Json; 128 | end; 129 | 130 | { TJsonWrapper } 131 | 132 | procedure TJsonWrapper.ArrayAdd(JArr: TJSONValue; Value: TJSONValue); 133 | begin 134 | {$IFDEF FPC} 135 | TJSONArray(JArr).Add(Value); 136 | {$ELSE} 137 | TJSONArray(JArr).AddElement(Value); 138 | {$ENDIF} 139 | end; 140 | 141 | function TJsonWrapper.ArrayGet(JArr: TJSONValue; Index: Integer): TJSONValue; 142 | begin 143 | Result := TJSONArray(JArr).Items[Index]; 144 | end; 145 | 146 | function TJsonWrapper.ArrayLength(JArr: TJSONValue): Integer; 147 | begin 148 | Result := TJSONArray(JArr).Count; 149 | end; 150 | 151 | function TJsonWrapper.BooleanFromJsonValue(Value: TJSONValue): Boolean; 152 | begin 153 | if IsBoolean(Value) then 154 | Result := TJSONBool(Value).AsBoolean 155 | else 156 | Result := False; 157 | end; 158 | 159 | function TJsonWrapper.BooleanToJsonValue(const Value: Boolean): TJSONValue; 160 | begin 161 | {$IFDEF FPC} 162 | if Value then 163 | Result := TJSONBool.Create(True) 164 | else 165 | Result := TJSONBool.Create(False); 166 | {$ELSE} 167 | if Value then 168 | Result := TJSONTrue.Create 169 | else 170 | Result := TJSONFalse.Create; 171 | {$ENDIF} 172 | end; 173 | 174 | function TJsonWrapper.CreateArray: TJSONValue; 175 | begin 176 | Result := TJSONArray.Create; 177 | end; 178 | 179 | function TJsonWrapper.CreateNull: TJSONValue; 180 | begin 181 | Result := TJSONNull.Create; 182 | end; 183 | 184 | function TJsonWrapper.CreateObject: TJSONValue; 185 | begin 186 | Result := TJSONObject.Create; 187 | end; 188 | 189 | function TJsonWrapper.DoubleFromJsonValue(Value: TJSONValue): Double; 190 | begin 191 | Result := 0; 192 | if IsNumber(Value) then 193 | {$IFDEF FPC} 194 | Result := TJSONNumber(Value).AsFloat; 195 | {$ELSE} 196 | Result := TJSONNumber(Value).AsDouble; 197 | {$ENDIF} 198 | end; 199 | 200 | function TJsonWrapper.DoubleToJsonValue(const Value: Double): TJSONValue; 201 | begin 202 | {$IFDEF FPC} 203 | Result := TJSONFloatNumber.Create(Value); 204 | {$ELSE} 205 | Result := TJSONNumber.Create(Value); 206 | {$ENDIF} 207 | end; 208 | 209 | function TJsonWrapper.Int64FromJsonValue(Value: TJSONValue): Int64; 210 | begin 211 | Result := 0; 212 | {$IFDEF FPC} 213 | if IsNumber(Value) then 214 | Result := TJSONNumber(Value).AsInt64; 215 | {$ELSE} 216 | if IsNumber(Value) and not IsFloatingPoint(TJSONNumber(Value).Value) then 217 | Result := TJSONNumber(Value).AsInt64; 218 | {$ENDIF} 219 | end; 220 | 221 | function TJsonWrapper.Int64ToJsonValue(const Value: Int64): TJSONValue; 222 | begin 223 | {$IFDEF FPC} 224 | Result := TJSONInt64Number.Create(Value); 225 | {$ELSE} 226 | Result := TJSONNumber.Create(Value); 227 | {$ENDIF} 228 | end; 229 | 230 | function TJsonWrapper.IntegerFromJsonValue(Value: TJSONValue): Integer; 231 | begin 232 | Result := 0; 233 | {$IFDEF FPC} 234 | if IsNumber(Value) then 235 | Result := TJSONNumber(Value).AsInteger; 236 | {$ELSE} 237 | if IsNumber(Value) and not IsFloatingPoint(TJSONNumber(Value).Value) then 238 | Result := TJSONNumber(Value).AsInt; 239 | {$ENDIF} 240 | end; 241 | 242 | function TJsonWrapper.IntegerToJsonValue(const Value: Integer): TJSONValue; 243 | begin 244 | {$IFDEF FPC} 245 | Result := TJSONIntegerNumber.Create(Value); 246 | {$ELSE} 247 | Result := TJSONNumber.Create(Value); 248 | {$ENDIF} 249 | end; 250 | 251 | function TJsonWrapper.IsArray(Value: TJSONValue): Boolean; 252 | begin 253 | Result := Value is TJSONArray; 254 | end; 255 | 256 | function TJsonWrapper.IsBoolean(Value: TJSONValue): Boolean; 257 | begin 258 | Result := Value is TJSONBool; 259 | end; 260 | 261 | function TJsonWrapper.IsFloatingPoint(const Value: string): Boolean; 262 | var 263 | DotPos: Integer; 264 | DotMinus: Integer; 265 | begin 266 | DotPos := Pos('.', Value); 267 | DotMinus := Pos('-', Value); 268 | Result := (DotPos > 0) or (DotMinus > 2); 269 | // TODO: There might be numbers with minus and still integer, e.g. 100e-1 270 | end; 271 | 272 | function TJsonWrapper.IsNull(Value: TJSONValue): Boolean; 273 | begin 274 | Result := Value is TJSONNull; 275 | end; 276 | 277 | function TJsonWrapper.IsNumber(Value: TJSONValue): Boolean; 278 | begin 279 | Result := Value is TJSONNumber; 280 | end; 281 | 282 | function TJsonWrapper.IsObject(Value: TJSONValue): Boolean; 283 | begin 284 | Result := Value is TJSONObject; 285 | end; 286 | 287 | function TJsonWrapper.IsString(Value: TJSONValue): Boolean; 288 | begin 289 | Result := Value is TJSONString; 290 | end; 291 | 292 | function TJsonWrapper.JsonToJsonValue(const Value: string): TJSONValue; 293 | begin 294 | {$IFDEF FPC} 295 | Result := fpjson.GetJSON(Value); 296 | {$ELSE} 297 | Result := TJSONObject.ParseJSONValue(Value); 298 | {$ENDIF} 299 | end; 300 | 301 | function TJsonWrapper.JsonValueToJson(Value: TJSONValue): string; 302 | begin 303 | {$IFDEF FPC} 304 | Result := Value.AsJSON; 305 | {$ELSE} 306 | Result := Value.ToString; 307 | {$ENDIF} 308 | end; 309 | 310 | procedure TJsonWrapper.ObjAddProp(JObj: TJSONValue; const Name: string; Value: TJSONValue); 311 | begin 312 | {$IFDEF FPC} 313 | TJSONObject(JObj).Add(Name, Value); 314 | {$ELSE} 315 | TJSONObject(JObj).AddPair(Name, Value); 316 | {$ENDIF} 317 | end; 318 | 319 | function TJsonWrapper.ObjContains(JObj: TJSONValue; const Name: string; out Value: TJSONValue): Boolean; 320 | begin 321 | {$IFDEF FPC} 322 | Value := TJSONObject(JObj).Find(Name); 323 | {$ELSE} 324 | Value := TJSONObject(JObj).GetValue(Name); 325 | {$ENDIF} 326 | Result := Value <> nil; 327 | end; 328 | 329 | function TJsonWrapper.StringFromJsonValue(Value: TJSONValue): string; 330 | begin 331 | if IsString(Value) then 332 | Result := TJSONString(Value).Value 333 | else 334 | Result := ''; 335 | end; 336 | 337 | function TJsonWrapper.StringToJsonValue(const Value: string): TJSONValue; 338 | begin 339 | Result := TJSONString.Create(Value); 340 | end; 341 | 342 | { TCustomJsonConverter } 343 | 344 | function TCustomJsonConverter.BooleanFromJson(Source: string): Boolean; 345 | var 346 | JValue: TJSONValue; 347 | begin 348 | JValue := JsonToJsonValue(Source); 349 | try 350 | Result := BooleanFromJsonValue(JValue); 351 | finally 352 | JValue.Free; 353 | end; 354 | end; 355 | 356 | function TCustomJsonConverter.BooleanFromJsonValue(Value: TJSONValue): Boolean; 357 | begin 358 | Result := Json.BooleanFromJsonValue(Value); 359 | end; 360 | 361 | function TCustomJsonConverter.BooleanToJson(const Source: Boolean): string; 362 | var 363 | JValue: TJSONValue; 364 | begin 365 | JValue := BooleanToJsonValue(Source); 366 | try 367 | Result := JsonValueToJson(JValue); 368 | finally 369 | JValue.Free; 370 | end; 371 | end; 372 | 373 | function TCustomJsonConverter.BooleanToJsonValue(const Value: Boolean): TJSONValue; 374 | begin 375 | Result := Json.BooleanToJsonValue(Value); 376 | end; 377 | 378 | constructor TCustomJsonConverter.Create(AJson: TJsonWrapper); 379 | begin 380 | inherited Create; 381 | FJson := AJson; 382 | end; 383 | 384 | function TCustomJsonConverter.DoubleFromJson(const Source: string): Double; 385 | var 386 | JValue: TJSONValue; 387 | begin 388 | JValue := JsonToJsonValue(Source); 389 | try 390 | Result := DoubleFromJsonValue(JValue); 391 | finally 392 | JValue.Free; 393 | end; 394 | end; 395 | 396 | function TCustomJsonConverter.DoubleFromJsonValue(Value: TJSONValue): Double; 397 | begin 398 | Result := Json.DoubleFromJsonValue(Value); 399 | end; 400 | 401 | function TCustomJsonConverter.DoubleToJson(const Source: Double): string; 402 | var 403 | JValue: TJSONValue; 404 | begin 405 | JValue := DoubleToJsonValue(Source); 406 | try 407 | Result := JsonValueToJson(JValue); 408 | finally 409 | JValue.Free; 410 | end; 411 | end; 412 | 413 | function TCustomJsonConverter.DoubleToJsonValue(const Value: Double): TJSONValue; 414 | begin 415 | Result := Json.DoubleToJsonValue(Value); 416 | end; 417 | 418 | function TCustomJsonConverter.TBytesFromJson(Source: string): TBytes; 419 | var 420 | JValue: TJSONValue; 421 | begin 422 | JValue := JsonToJsonValue(Source); 423 | try 424 | Result := TBytesFromJsonValue(JValue); 425 | finally 426 | JValue.Free; 427 | end; 428 | end; 429 | 430 | function TCustomJsonConverter.TBytesFromJsonValue(Value: TJSONValue): TBytes; 431 | begin 432 | Result := OpenApiUtils.DecodeBase64(StringFromJsonValue(Value)); 433 | end; 434 | 435 | function TCustomJsonConverter.TBytesToJson(const Source: TBytes): string; 436 | var 437 | JValue: TJSONValue; 438 | begin 439 | JValue := TBytesToJsonValue(Source); 440 | try 441 | Result := JsonValueToJson(JValue); 442 | finally 443 | JValue.Free; 444 | end; 445 | end; 446 | 447 | function TCustomJsonConverter.TBytesToJsonValue(const Value: TBytes): TJSONValue; 448 | begin 449 | Result := StringToJsonValue(OpenApiUtils.EncodeBase64(Value)); 450 | end; 451 | 452 | function TCustomJsonConverter.TDateFromJson(Source: string): TDate; 453 | var 454 | JValue: TJSONValue; 455 | begin 456 | JValue := JsonToJsonValue(Source); 457 | try 458 | Result := TDateFromJsonValue(JValue); 459 | finally 460 | JValue.Free; 461 | end; 462 | end; 463 | 464 | function TCustomJsonConverter.TDateFromJsonValue(Value: TJSONValue): TDate; 465 | begin 466 | Result := OpenApiUtils.ISOToDate(StringFromJsonValue(Value)); 467 | end; 468 | 469 | function TCustomJsonConverter.TDateTimeFromJson(Source: string): TDateTime; 470 | var 471 | JValue: TJSONValue; 472 | begin 473 | JValue := JsonToJsonValue(Source); 474 | try 475 | Result := TDateTimeFromJsonValue(JValue); 476 | finally 477 | JValue.Free; 478 | end; 479 | end; 480 | 481 | function TCustomJsonConverter.TDateTimeFromJsonValue(Value: TJSONValue): TDateTime; 482 | begin 483 | Result := OpenApiUtils.ISOToDateTime(StringFromJsonValue(Value)); 484 | end; 485 | 486 | function TCustomJsonConverter.TDateTimeToJson(const Source: TDateTime): string; 487 | var 488 | JValue: TJSONValue; 489 | begin 490 | JValue := TDateTimeToJsonValue(Source); 491 | try 492 | Result := JsonValueToJson(JValue); 493 | finally 494 | JValue.Free; 495 | end; 496 | end; 497 | 498 | function TCustomJsonConverter.TDateTimeToJsonValue(const Value: TDateTime): TJSONValue; 499 | begin 500 | Result := StringToJsonValue(OpenApiUtils.DateTimeToISO(Value)); 501 | end; 502 | 503 | function TCustomJsonConverter.TDateToJson(const Source: TDate): string; 504 | var 505 | JValue: TJSONValue; 506 | begin 507 | JValue := TDateToJsonValue(Source); 508 | try 509 | Result := JsonValueToJson(JValue); 510 | finally 511 | JValue.Free; 512 | end; 513 | end; 514 | 515 | function TCustomJsonConverter.TDateToJsonValue(const Value: TDate): TJSONValue; 516 | begin 517 | Result := StringToJsonValue(OpenApiUtils.DateToISO(Value)); 518 | end; 519 | 520 | function TCustomJsonConverter.Int64FromJson(Source: string): Int64; 521 | var 522 | JValue: TJSONValue; 523 | begin 524 | JValue := JsonToJsonValue(Source); 525 | try 526 | Result := Int64FromJsonValue(JValue); 527 | finally 528 | JValue.Free; 529 | end; 530 | end; 531 | 532 | function TCustomJsonConverter.Int64FromJsonValue(Value: TJSONValue): Int64; 533 | begin 534 | Result := Json.Int64FromJsonValue(Value); 535 | end; 536 | 537 | function TCustomJsonConverter.Int64ToJson(const Source: Int64): string; 538 | var 539 | JValue: TJSONValue; 540 | begin 541 | JValue := Int64ToJsonValue(Source); 542 | try 543 | Result := JsonValueToJson(JValue); 544 | finally 545 | JValue.Free; 546 | end; 547 | end; 548 | 549 | function TCustomJsonConverter.Int64ToJsonValue(const Value: Int64): TJSONValue; 550 | begin 551 | Result := Json.Int64ToJsonValue(Value); 552 | end; 553 | 554 | function TCustomJsonConverter.IntegerFromJson(Source: string): Integer; 555 | var 556 | JValue: TJSONValue; 557 | begin 558 | JValue := JsonToJsonValue(Source); 559 | try 560 | Result := IntegerFromJsonValue(JValue); 561 | finally 562 | JValue.Free; 563 | end; 564 | end; 565 | 566 | function TCustomJsonConverter.IntegerFromJsonValue(Value: TJSONValue): Integer; 567 | begin 568 | Result := Json.IntegerFromJsonValue(Value); 569 | end; 570 | 571 | function TCustomJsonConverter.IntegerToJson(const Source: Integer): string; 572 | var 573 | JValue: TJSONValue; 574 | begin 575 | JValue := IntegerToJsonValue(Source); 576 | try 577 | Result := JsonValueToJson(JValue); 578 | finally 579 | JValue.Free; 580 | end; 581 | end; 582 | 583 | function TCustomJsonConverter.IntegerToJsonValue(const Value: Integer): TJSONValue; 584 | begin 585 | Result := Json.IntegerToJsonValue(Value); 586 | end; 587 | 588 | function TCustomJsonConverter.JsonToJsonValue(const Value: string): TJSONValue; 589 | begin 590 | Result := Json.JsonToJsonValue(Value); 591 | end; 592 | 593 | function TCustomJsonConverter.JsonValueToJson(Value: TJSONValue): string; 594 | begin 595 | Result := Json.JsonValueToJson(Value); 596 | end; 597 | 598 | function TCustomJsonConverter.StringFromJson(Source: string): string; 599 | var 600 | JValue: TJSONValue; 601 | begin 602 | JValue := JsonToJsonValue(Source); 603 | try 604 | Result := StringFromJsonValue(JValue); 605 | finally 606 | JValue.Free; 607 | end; 608 | end; 609 | 610 | function TCustomJsonConverter.StringFromJsonValue(Value: TJSONValue): string; 611 | begin 612 | Result := Json.StringFromJsonValue(Value); 613 | end; 614 | 615 | function TCustomJsonConverter.StringToJson(const Source: string): string; 616 | var 617 | JValue: TJSONValue; 618 | begin 619 | JValue := StringToJsonValue(Source); 620 | try 621 | Result := JsonValueToJson(JValue); 622 | finally 623 | JValue.Free; 624 | end; 625 | end; 626 | 627 | function TCustomJsonConverter.StringToJsonValue(const Value: string): TJSONValue; 628 | begin 629 | Result := Json.StringToJsonValue(Value); 630 | end; 631 | 632 | constructor TCustomJsonConverter.Create; 633 | begin 634 | Create(_Json); 635 | end; 636 | 637 | initialization 638 | _Json := TJsonWrapper.Create; 639 | finalization 640 | _Json.Free; 641 | end. 642 | -------------------------------------------------------------------------------- /Source/OpenApiRest.pas: -------------------------------------------------------------------------------- 1 | unit OpenApiRest; 2 | 3 | interface 4 | 5 | uses 6 | SysUtils, Classes, DateUtils, 7 | OpenApiJson; 8 | 9 | type 10 | TCustomJsonConverter = OpenApiJson.TCustomJsonConverter; 11 | 12 | IRestResponse = interface 13 | ['{C2CE5CD8-FA9F-442F-9980-988A2A0EFF3D}'] 14 | function StatusCode: Integer; 15 | function ContentAsString: string; 16 | function ContentAsBytes: TBytes; 17 | function GetHeader(const Name: string): string; 18 | end; 19 | 20 | IRestRequest = interface 21 | ['{55328D2F-FC30-48C7-9578-5A8A9152E4DA}'] 22 | procedure SetUrl(const Url: string); 23 | procedure SetMethod(const Method: string); 24 | procedure AddQueryParam(const Name, Value: string); 25 | procedure AddUrlParam(const Name, Value: string); 26 | procedure AddHeader(const Name, Value: string); 27 | procedure AddBody(const Value: string); 28 | function Execute: IRestResponse; 29 | end; 30 | 31 | IRestRequestFactory = interface 32 | ['{3F581342-8522-44BD-8D42-1CAFE7DD7CC1}'] 33 | function CreateRequest: IRestRequest; 34 | end; 35 | 36 | TRestRequest = class(TInterfacedObject, IRestRequest) 37 | private 38 | FUrl: string; 39 | FMethod: string; 40 | FQueryParams: TStrings; 41 | FUrlParams: TStrings; 42 | FHeaders: TStrings; 43 | FBody: string; 44 | protected 45 | function BuildUrl: string; 46 | function PercentEncode(const Value: string): string; virtual; 47 | property Body: string read FBody; 48 | property Method: string read FMethod; 49 | property Headers: TStrings read FHeaders; 50 | public 51 | constructor Create; 52 | destructor Destroy; override; 53 | procedure SetUrl(const Url: string); 54 | procedure SetMethod(const HttpMethod: string); 55 | procedure AddHeader(const Name, Value: string); 56 | procedure AddQueryParam(const Name, Value: string); virtual; 57 | procedure AddUrlParam(const Name, Value: string); virtual; 58 | procedure AddBody(const Value: string); virtual; 59 | function Execute: IRestResponse; virtual; abstract; 60 | end; 61 | 62 | EOpenApiClientException = class(Exception) 63 | private 64 | FResponse: IRestResponse; 65 | public 66 | constructor Create(const Msg: string; Response: IRestResponse); 67 | property Response: IRestResponse read FResponse; 68 | end; 69 | 70 | IRestConfig = interface 71 | ['{19651CF9-B9EB-44CA-BF22-802D0EBA6549}'] 72 | function GetAccessToken: string; 73 | function GetBaseUrl: string; 74 | procedure SetAccessToken(const Value: string); 75 | procedure SetBaseUrl(const Value: string); 76 | function GetRequestFactory: IRestRequestFactory; 77 | procedure SetRequestFactory(const Value: IRestRequestFactory); 78 | 79 | property BaseUrl: string read GetBaseUrl write SetBaseUrl; 80 | property AccessToken: string read GetAccessToken write SetAccessToken; 81 | property RequestFactory: IRestRequestFactory read GetRequestFactory write SetRequestFactory; 82 | end; 83 | 84 | TCustomRestConfig = class(TInterfacedObject, IRestConfig) 85 | private 86 | FBaseUrl: string; 87 | FAccessToken: string; 88 | FRequestFactory: IRestRequestFactory; 89 | function GetAccessToken: string; 90 | function GetBaseUrl: string; 91 | procedure SetAccessToken(const Value: string); 92 | procedure SetBaseUrl(const Value: string); 93 | function GetRequestFactory: IRestRequestFactory; 94 | procedure SetRequestFactory(const Value: IRestRequestFactory); 95 | public 96 | constructor Create; 97 | property BaseUrl: string read GetBaseUrl write SetBaseUrl; 98 | property AccessToken: string read GetAccessToken write SetAccessToken; 99 | property RequestFactory: IRestRequestFactory read GetRequestFactory write SetRequestFactory; 100 | end; 101 | 102 | TCustomRestService = class(TInterfacedObject) 103 | private 104 | FConfig: IRestConfig; 105 | FConverter: TCustomJsonConverter; 106 | function SanitizedBaseUrl: string; 107 | protected 108 | procedure CheckError(Response: IRestResponse); 109 | function CreateConverter: TCustomJsonConverter; virtual; 110 | function Converter: TCustomJsonConverter; 111 | public 112 | constructor Create(Config: IRestConfig); reintroduce; 113 | destructor Destroy; override; 114 | function CreateRequest(const UrlPath, HttpMethod: string): IRestRequest; 115 | property Config: IRestConfig read FConfig; 116 | end; 117 | 118 | IRestClient = interface 119 | ['{EC814857-4D41-49E1-BEBB-E2C8A3DFB8B3}'] 120 | function GetConfig: IRestConfig; 121 | 122 | property Config: IRestConfig read GetConfig; 123 | end; 124 | 125 | TCustomRestClient = class(TInterfacedObject, IRestClient) 126 | private 127 | FConfig: IRestConfig; 128 | protected 129 | function GetConfig: IRestConfig; 130 | public 131 | constructor Create(Config: IRestConfig); 132 | property Config: IRestConfig read GetConfig; 133 | end; 134 | 135 | ITokenData = interface 136 | ['{C0D7EA65-A432-426F-BBCF-6E3723622266}'] 137 | function GetAccessToken: string; 138 | function GetExpirationTime: TDateTime; 139 | 140 | property AccessToken: string read GetAccessToken; 141 | property ExpirationTime: TDateTime read GetExpirationTime; 142 | end; 143 | 144 | ITokenProvider = interface 145 | ['{0951C910-85BC-4F3E-8835-11EA60F96019}'] 146 | function RetrieveToken: ITokenData; 147 | end; 148 | 149 | TTokenData = class(TInterfacedObject, ITokenData) 150 | private 151 | FAccessToken: string; 152 | FExpirationTime: TDateTime; 153 | function GetAccessToken: string; 154 | function GetExpirationTime: TDateTime; 155 | public 156 | constructor Create(const AccessToken: string; ExpiresIn: Integer); 157 | property AccessToken: string read GetAccessToken; 158 | property ExpirationTime: TDateTime read GetExpirationTime; 159 | end; 160 | 161 | IClientCredencialsTokenProvider = interface(ITokenProvider) 162 | function GetClientId: string; 163 | function GetClientSecret: string; 164 | function GetScope: string; 165 | function GetTokenEndpoint: string; 166 | procedure SetClientId(const Value: string); 167 | procedure SetClientSecret(const Value: string); 168 | procedure SetScope(const Value: string); 169 | procedure SetTokenEndpoint(const Value: string); 170 | 171 | property TokenEndpoint: string read GetTokenEndpoint write SetTokenEndpoint; 172 | property ClientId: string read GetClientId write SetClientId; 173 | property ClientSecret: string read GetClientSecret write SetClientSecret; 174 | property Scope: string read GetScope write SetScope; 175 | end; 176 | 177 | TClientCredentialsTokenProvider = class(TInterfacedObject, IClientCredencialsTokenProvider) 178 | private 179 | FClientId: string; 180 | FClientSecret: string; 181 | FTokenEndpoint: string; 182 | FScope: string; 183 | FJson: TJsonWrapper; 184 | FRequestFactory: IRestRequestFactory; 185 | function GetClientId: string; 186 | function GetClientSecret: string; 187 | function GetScope: string; 188 | function GetTokenEndpoint: string; 189 | procedure SetClientId(const Value: string); 190 | procedure SetClientSecret(const Value: string); 191 | procedure SetScope(const Value: string); 192 | procedure SetTokenEndpoint(const Value: string); 193 | protected 194 | procedure Init; virtual; 195 | property Json: TJsonWrapper read FJson; 196 | property RequestFactory: IRestRequestFactory read FRequestFactory; 197 | public 198 | constructor Create; overload; 199 | constructor Create(JsonWrapper: TJsonWrapper; ReqFactory: IRestRequestFactory); overload; 200 | function RetrieveToken: ITokenData; 201 | property TokenEndpoint: string read GetTokenEndpoint write SetTokenEndpoint; 202 | property ClientId: string read GetClientId write SetClientId; 203 | property ClientSecret: string read GetClientSecret write SetClientSecret; 204 | property Scope: string read GetScope write SetScope; 205 | end; 206 | 207 | var 208 | DefaultRequestFactory: IRestRequestFactory; 209 | 210 | implementation 211 | 212 | uses 213 | // refactor this later to allow setting the default request factory based on Pascal language being used 214 | {$IFDEF FPC} 215 | OpenApiFpc, 216 | {$ELSE} 217 | OpenApiHttp, 218 | {$ENDIF} 219 | OpenApiUtils; 220 | 221 | { TRestService } 222 | 223 | procedure TCustomRestService.CheckError(Response: IRestResponse); 224 | begin 225 | if (Response.StatusCode < 200) or (Response.StatusCode >= 300) then 226 | raise EOpenApiClientException.Create('Request failed', Response); 227 | end; 228 | 229 | function TCustomRestService.Converter: TCustomJsonConverter; 230 | begin 231 | if FConverter = nil then 232 | FConverter := CreateConverter; 233 | Result := FConverter; 234 | end; 235 | 236 | constructor TCustomRestService.Create(Config: IRestConfig); 237 | begin 238 | inherited Create; 239 | Self.FConfig := Config; 240 | end; 241 | 242 | function TCustomRestService.CreateConverter: TCustomJsonConverter; 243 | begin 244 | Result := TCustomJsonConverter.Create; 245 | end; 246 | 247 | function TCustomRestService.CreateRequest(const UrlPath, HttpMethod: string): IRestRequest; 248 | var 249 | Url: string; 250 | begin 251 | Result := Config.RequestFactory.CreateRequest; 252 | Url := SanitizedBaseUrl; 253 | if (Length(UrlPath) > 0) and (UrlPath[1] <> '/') then 254 | Url := Url + '/'; 255 | Url := Url + UrlPath; 256 | Result.SetUrl(Url); 257 | Result.SetMethod(HttpMethod); 258 | if Config.AccessToken <> '' then 259 | Result.AddHeader('Authorization', 'Bearer ' + Config.AccessToken); 260 | end; 261 | 262 | destructor TCustomRestService.Destroy; 263 | begin 264 | FConverter.Free; 265 | inherited; 266 | end; 267 | 268 | function TCustomRestService.SanitizedBaseUrl: string; 269 | var 270 | BaseUrl: string; 271 | begin 272 | BaseUrl := Config.BaseUrl; 273 | if BaseUrl = '' then 274 | raise EArgumentException.Create('Invalid BaseUrl'); 275 | 276 | // Normalize BaseUrl by removing trailing slash 277 | if (Length(BaseUrl) > 0) and (BaseUrl[Length(BaseUrl)] = '/') then 278 | BaseUrl := Copy(BaseUrl, 1, Length(BaseUrl) - 1); 279 | Result := BaseUrl; 280 | end; 281 | 282 | { TRestRequest } 283 | 284 | procedure TRestRequest.AddBody(const Value: string); 285 | begin 286 | FBody := Value; 287 | end; 288 | 289 | procedure TRestRequest.AddHeader(const Name, Value: string); 290 | begin 291 | FHeaders.Values[Name] := Value; 292 | end; 293 | 294 | procedure TRestRequest.AddQueryParam(const Name, Value: string); 295 | begin 296 | FQueryParams.Values[Name] := Value; 297 | end; 298 | 299 | procedure TRestRequest.AddUrlParam(const Name, Value: string); 300 | begin 301 | FUrlParams.Values[Name] := Value; 302 | end; 303 | 304 | function TRestRequest.BuildUrl: string; 305 | var 306 | I: Integer; 307 | Name, Value: string; 308 | Query: string; 309 | begin 310 | Result := FUrl; 311 | for I := 0 to FUrlParams.Count - 1 do 312 | begin 313 | Name := FUrlParams.Names[I]; 314 | Value := PercentEncode(FUrlParams.ValueFromIndex[I]); 315 | 316 | Result := StringReplace(Result, '{' + Name + '}', Value, [rfIgnoreCase, rfReplaceAll]); 317 | end; 318 | 319 | Query := ''; 320 | for I := 0 to FQueryParams.Count - 1 do 321 | OpenApiUtils.AppendQueryParam(Query, FQueryParams.Names[I], FQueryParams.ValueFromIndex[I]); 322 | 323 | if Query <> '' then 324 | Result := Result + '?' + Query; 325 | end; 326 | 327 | constructor TRestRequest.Create; 328 | begin 329 | inherited Create; 330 | FQueryParams := TStringList.Create; 331 | FUrlParams := TStringList.Create; 332 | FHeaders := TStringList.Create; 333 | end; 334 | 335 | destructor TRestRequest.Destroy; 336 | begin 337 | FHeaders.Free; 338 | FUrlParams.Free; 339 | FQueryParams.Free; 340 | inherited; 341 | end; 342 | 343 | function TRestRequest.PercentEncode(const Value: string): string; 344 | begin 345 | Result := OpenApiUtils.PercentEncode(Value); 346 | end; 347 | 348 | procedure TRestRequest.SetMethod(const HttpMethod: string); 349 | begin 350 | FMethod := HttpMethod; 351 | end; 352 | 353 | procedure TRestRequest.SetUrl(const Url: string); 354 | begin 355 | FUrl := Url; 356 | end; 357 | 358 | { EOpenApiClientException } 359 | 360 | constructor EOpenApiClientException.Create(const Msg: string; Response: IRestResponse); 361 | var 362 | Content: string; 363 | ErrorMsg: string; 364 | begin 365 | ErrorMsg := Msg + sLineBreak + 'status: ' + IntToStr(Response.StatusCode); 366 | Content := Response.ContentAsString; 367 | if Content <> '' then 368 | ErrorMsg := ErrorMsg + sLineBreak + 'Response: ' + Copy(Content, 1, 512); 369 | FResponse := Response; 370 | inherited Create(ErrorMsg); 371 | end; 372 | 373 | { TRestConfig } 374 | 375 | constructor TCustomRestConfig.Create; 376 | begin 377 | inherited Create; 378 | FRequestFactory := DefaultRequestFactory; 379 | end; 380 | 381 | function TCustomRestConfig.GetAccessToken: string; 382 | begin 383 | Result := FAccessToken; 384 | end; 385 | 386 | function TCustomRestConfig.GetBaseUrl: string; 387 | begin 388 | Result := FBaseUrl; 389 | end; 390 | 391 | function TCustomRestConfig.GetRequestFactory: IRestRequestFactory; 392 | begin 393 | Result := FRequestFactory; 394 | end; 395 | 396 | procedure TCustomRestConfig.SetAccessToken(const Value: string); 397 | begin 398 | FAccessToken := Value; 399 | end; 400 | 401 | procedure TCustomRestConfig.SetBaseUrl(const Value: string); 402 | begin 403 | FBaseUrl := Value; 404 | end; 405 | 406 | procedure TCustomRestConfig.SetRequestFactory(const Value: IRestRequestFactory); 407 | begin 408 | FRequestFactory := Value; 409 | end; 410 | 411 | { TCustomRestClient } 412 | 413 | constructor TCustomRestClient.Create(Config: IRestConfig); 414 | begin 415 | inherited Create; 416 | FConfig := Config; 417 | end; 418 | 419 | function TCustomRestClient.GetConfig: IRestConfig; 420 | begin 421 | Result := FConfig; 422 | end; 423 | 424 | { TTokenData } 425 | 426 | constructor TTokenData.Create(const AccessToken: string; ExpiresIn: Integer); 427 | begin 428 | inherited Create; 429 | FAccessToken := AccessToken; 430 | if ExpiresIn > 0 then 431 | FExpirationTime := IncSecond(Now, ExpiresIn) 432 | else 433 | FExpirationTime := MaxDateTime; 434 | end; 435 | 436 | function TTokenData.GetAccessToken: string; 437 | begin 438 | Result := FAccessToken; 439 | end; 440 | 441 | function TTokenData.GetExpirationTime: TDateTime; 442 | begin 443 | Result := FExpirationTime; 444 | end; 445 | 446 | { TClientCredentialsTokenProvider } 447 | 448 | constructor TClientCredentialsTokenProvider.Create; 449 | begin 450 | inherited Create; 451 | FJson := JsonWrapper; 452 | FRequestFactory := DefaultRequestFactory; 453 | end; 454 | 455 | constructor TClientCredentialsTokenProvider.Create(JsonWrapper: TJsonWrapper; ReqFactory: IRestRequestFactory); 456 | begin 457 | inherited Create; 458 | FJson := JsonWrapper; 459 | FRequestFactory := ReqFactory; 460 | Init; 461 | end; 462 | 463 | function TClientCredentialsTokenProvider.GetClientId: string; 464 | begin 465 | Result := FClientId; 466 | end; 467 | 468 | function TClientCredentialsTokenProvider.GetClientSecret: string; 469 | begin 470 | Result := FClientSecret; 471 | end; 472 | 473 | function TClientCredentialsTokenProvider.GetScope: string; 474 | begin 475 | Result := FScope; 476 | end; 477 | 478 | function TClientCredentialsTokenProvider.GetTokenEndpoint: string; 479 | begin 480 | Result := FTokenEndpoint; 481 | end; 482 | 483 | procedure TClientCredentialsTokenProvider.Init; 484 | begin 485 | end; 486 | 487 | function TClientCredentialsTokenProvider.RetrieveToken: ITokenData; 488 | var 489 | Request: IRestRequest; 490 | Response: IRestResponse; 491 | JObj: TJSONValue; 492 | JProp: TJSONValue; 493 | JErrorDescription: TJsonValue; 494 | ErrorDescription: string; 495 | AccessToken: string; 496 | ExpiresIn: Integer; 497 | Params: string; 498 | begin 499 | Request := RequestFactory.CreateRequest; 500 | Request.SetUrl(TokenEndpoint); 501 | Request.SetMethod('POST'); 502 | 503 | Request.AddHeader('Content-Type', 'application/x-www-form-urlencoded'); 504 | 505 | Params := ''; 506 | OpenApiUtils.AppendQueryParam(Params, 'grant_type', 'client_credentials'); 507 | OpenApiUtils.AppendQueryParam(Params, 'client_id', ClientId); 508 | OpenApiUtils.AppendQueryParam(Params, 'client_secret', ClientSecret); 509 | if Scope <> '' then 510 | OpenApiUtils.AppendQueryParam(Params, 'scope', Scope); 511 | Request.AddBody(Params); 512 | 513 | // Request.AddHeader('Authorization', Basic); 514 | 515 | Response := Request.Execute; 516 | if (Response.StatusCode < 200) or (Response.StatusCode >= 300) then 517 | raise EOpenApiClientException.Create('Token request failed with status code ' + IntToStr(Response.StatusCode), Response); 518 | if not SameText(Response.GetHeader('Content-Type'), 'application/json') then 519 | raise EOpenApiClientException.Create('Token requested failed: unexpected response content type', Response); 520 | 521 | Result := nil; 522 | JProp := nil; 523 | JErrorDescription := nil; 524 | JObj := Json.JsonToJsonValue(Response.ContentAsString); 525 | try 526 | if Json.IsObject(JObj) then 527 | begin 528 | if Json.ObjContains(JObj, 'error', JProp) then 529 | begin 530 | if Json.ObjContains(JObj, 'error_description', JErrorDescription) then 531 | ErrorDescription := Json.StringFromJsonValue(JErrorDescription); 532 | if ErrorDescription <> '' then 533 | ErrorDescription := ' - ' + ErrorDescription; 534 | raise EOpenApiClientException.Create(Format('Token request failed: %s%s', 535 | [Json.StringFromJsonValue(JProp), ErrorDescription]), Response); 536 | end; 537 | 538 | if Json.ObjContains(JObj, 'access_token', JProp) then 539 | AccessToken := Json.StringFromJsonValue(JProp); 540 | if Json.ObjContains(JObj, 'expires_in', JProp) then 541 | ExpiresIn := Json.IntegerFromJsonValue(JProp) 542 | else 543 | ExpiresIn := 0; 544 | Result := TTokenData.Create(AccessToken, ExpiresIn); 545 | end; 546 | finally 547 | JObj.Free; 548 | end; 549 | end; 550 | 551 | procedure TClientCredentialsTokenProvider.SetClientId(const Value: string); 552 | begin 553 | FClientId := Value; 554 | end; 555 | 556 | procedure TClientCredentialsTokenProvider.SetClientSecret(const Value: string); 557 | begin 558 | FClientSecret := Value; 559 | end; 560 | 561 | procedure TClientCredentialsTokenProvider.SetScope(const Value: string); 562 | begin 563 | FScope := Value; 564 | end; 565 | 566 | procedure TClientCredentialsTokenProvider.SetTokenEndpoint(const Value: string); 567 | begin 568 | FTokenEndpoint := Value; 569 | end; 570 | 571 | end. 572 | -------------------------------------------------------------------------------- /Source/OpenApiSparkle.pas: -------------------------------------------------------------------------------- 1 | unit OpenApiSparkle; 2 | 3 | interface 4 | 5 | uses 6 | SysUtils, OpenApiRest, 7 | Sparkle.Http.Client; 8 | 9 | type 10 | TSparkleRestRequest = class(TRestRequest) 11 | public 12 | function Execute: IRestResponse; override; 13 | end; 14 | 15 | TSparkleRestResponse = class(TInterfacedObject, IRestResponse) 16 | strict private 17 | FClient: THttpClient; 18 | FResponse: IHttpResponse; 19 | public 20 | constructor Create(Response: IHttpResponse; Client: THttpClient); 21 | destructor Destroy; override; 22 | function StatusCode: Integer; 23 | function ContentAsString: string; 24 | function ContentAsBytes: TBytes; 25 | function GetHeader(const Name: string): string; 26 | end; 27 | 28 | TSparkleRestRequestFactory = class(TInterfacedObject, IRestRequestFactory) 29 | public 30 | function CreateRequest: IRestRequest; 31 | end; 32 | 33 | implementation 34 | 35 | { TSparkleRestRequestFactory } 36 | 37 | function TSparkleRestRequestFactory.CreateRequest: IRestRequest; 38 | begin 39 | Result := TSparkleRestRequest.Create; 40 | end; 41 | 42 | { TSparkleRestRequest } 43 | 44 | function TSparkleRestRequest.Execute: IRestResponse; 45 | var 46 | Request: THttpRequest; 47 | Response: IHttpResponse; 48 | Client: THttpClient; 49 | I: Integer; 50 | begin 51 | Client := THttpClient.Create; 52 | try 53 | Request := Client.CreateRequest; 54 | try 55 | Request.Uri := BuildUrl; 56 | Request.Method := Self.Method; 57 | if Body <> '' then 58 | Request.SetContent(TEncoding.UTF8.GetBytes(Body)); 59 | for I := 0 to Headers.Count - 1 do 60 | Request.Headers.SetValue(Headers.Names[I], Headers.ValueFromIndex[I]); 61 | 62 | Response := Client.Send(Request); 63 | Result := TSparkleRestResponse.Create(Response, Client); 64 | Client := nil; 65 | finally 66 | Request.Free; 67 | end; 68 | finally 69 | Client.Free; 70 | end; 71 | end; 72 | 73 | { TSparkleRestResponse } 74 | 75 | constructor TSparkleRestResponse.Create(Response: IHttpResponse; Client: THttpClient); 76 | begin 77 | inherited Create; 78 | FResponse := Response; 79 | FClient := Client; 80 | end; 81 | 82 | destructor TSparkleRestResponse.Destroy; 83 | begin 84 | FClient.Free; 85 | inherited; 86 | end; 87 | 88 | function TSparkleRestResponse.GetHeader(const Name: string): string; 89 | begin 90 | Result := FResponse.Headers.Get(Name); 91 | end; 92 | 93 | function TSparkleRestResponse.StatusCode: Integer; 94 | begin 95 | Result := FResponse.StatusCode; 96 | end; 97 | 98 | function TSparkleRestResponse.ContentAsBytes: TBytes; 99 | begin 100 | Result := FResponse.ContentAsBytes; 101 | end; 102 | 103 | function TSparkleRestResponse.ContentAsString: string; 104 | begin 105 | Result := TEncoding.UTF8.GetString(FResponse.ContentAsBytes); 106 | end; 107 | 108 | end. 109 | -------------------------------------------------------------------------------- /Source/OpenApiUtils.pas: -------------------------------------------------------------------------------- 1 | unit OpenApiUtils; 2 | 3 | {$IFDEF FPC}{$MODE Delphi}{$ENDIF} 4 | 5 | {$IFNDEF FPC} 6 | {$IF CompilerVersion >= 24} 7 | {$LEGACYIFEND ON} 8 | {$IFEND} 9 | {$ENDIF} 10 | 11 | interface 12 | 13 | uses 14 | SysUtils, DateUtils, Character; 15 | 16 | function PercentEncode(const S: string): string; 17 | procedure AppendQueryParam(var Query: string; const Name, Value: string); 18 | function EncodeBase64(const Input: TBytes): string; 19 | function DecodeBase64(const Input: string): TBytes; 20 | function DateTimeToISO(const Value: TDateTime): string; 21 | function DateToISO(const Value: TDate): string; 22 | function ISOToDateTime(const Value: string): TDateTime; 23 | function ISOToDate(const Value: string): TDate; 24 | 25 | resourcestring 26 | SInvalidDateFormat = 'Value %s is not a valid datetime'; 27 | 28 | implementation 29 | 30 | type 31 | TTimeZoneMode = (zmError, zmIgnore, zmAsUTC, zmAsLocal); 32 | 33 | TBase64EncodeTable = array[0..63] of Char; 34 | 35 | TPacket = packed record 36 | a: array[0..3] of Byte; 37 | end; 38 | 39 | TTimeZoneInfo = record 40 | HourOff: Integer; 41 | MinOff: Integer; 42 | HasTimeZone: Boolean; 43 | end; 44 | 45 | function NewTimeZoneInfo: TTimeZoneInfo; 46 | begin 47 | Result.HourOff := 0; 48 | Result.MinOff := 0; 49 | Result.HasTimeZone := False; 50 | end; 51 | 52 | function InternalEncodeBase64(const Input: TBytes; 53 | EncodeTable: TBase64EncodeTable; Padding: Boolean): string; 54 | var 55 | Output: string; 56 | 57 | procedure EncodePacket(const Packet: TPacket; NumChars: Integer; Idx: Integer); 58 | begin 59 | Output[Idx + 0] := EnCodeTable[Packet.a[0] shr 2]; 60 | Output[Idx + 1] := EnCodeTable[((Packet.a[0] shl 4) or (Packet.a[1] shr 4)) and $0000003f]; 61 | 62 | if NumChars < 2 then 63 | Output[Idx + 2] := '=' 64 | else 65 | Output[Idx + 2] := EnCodeTable[((Packet.a[1] shl 2) or (Packet.a[2] shr 6)) and $0000003f]; 66 | 67 | if NumChars < 3 then 68 | Output[Idx + 3] := '=' 69 | else 70 | Output[Idx + 3] := EnCodeTable[Packet.a[2] and $0000003f]; 71 | end; 72 | 73 | var 74 | I, K, J: Integer; 75 | Packet: TPacket; 76 | begin 77 | Output := ''; 78 | I := (Length(Input) div 3) * 4; 79 | if Length(Input) mod 3 > 0 then Inc(I, 4); 80 | SetLength(Output, I); 81 | J := 1; 82 | for I := 1 to Length(Input) div 3 do 83 | begin 84 | Packet.a[0] := Input[(I - 1) * 3]; 85 | Packet.a[1] := Input[(I - 1) * 3 + 1]; 86 | Packet.a[2] := Input[(I - 1) * 3 + 2]; 87 | Packet.a[3] := 0; 88 | EncodePacket(Packet, 3, J); 89 | Inc(J, 4); 90 | end; 91 | K := 0; 92 | 93 | Packet.a[0] := 0; 94 | Packet.a[1] := 0; 95 | Packet.a[2] := 0; 96 | Packet.a[3] := 0; 97 | 98 | for I := Length(Input) - (Length(Input) mod 3) + 1 to Length(Input) do 99 | begin 100 | Packet.a[K] := Byte(Input[I - 1]); 101 | Inc(K); 102 | if I = Length(Input) then 103 | EncodePacket(Packet, Length(Input) mod 3, J); 104 | end; 105 | 106 | if not Padding and (Length(Output) >= 2) then 107 | begin 108 | if Output[Length(Output) - 1] = '=' then 109 | SetLength(Output, Length(Output) - 2) 110 | else 111 | if Output[Length(Output)] = '=' then 112 | SetLength(Output, Length(Output) - 1); 113 | end; 114 | Result := Output; 115 | end; 116 | 117 | function EncodeBase64(const Input: TBytes): string; 118 | const 119 | EncodeTable: TBase64EncodeTable = 120 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 121 | 'abcdefghijklmnopqrstuvwxyz' + 122 | '0123456789+/'; 123 | begin 124 | Result := InternalEncodeBase64(Input, EncodeTable, True); 125 | end; 126 | 127 | function DecodeBase64(const Input: string): TBytes; 128 | var 129 | StrLen: Integer; 130 | const 131 | DecodeTable: array[#0..#127] of Integer = ( 132 | 61, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 133 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 134 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 62, 64, 63, 135 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 136 | 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 137 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 63, 138 | 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 139 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64); 140 | 141 | function DecodePacket(Idx: Integer; var nChars: Integer): TPacket; 142 | begin 143 | Result.a[0] := (DecodeTable[Input[Idx + 0]] shl 2) or 144 | (DecodeTable[Input[Idx + 1]] shr 4); 145 | NChars := 1; 146 | if (Idx + 2 <= StrLen) and (Input[Idx + 2] <> '=') then 147 | begin 148 | Inc(NChars); 149 | Result.a[1] := ((DecodeTable[Input[Idx + 1]] shl 4) or (DecodeTable[Input[Idx + 2]] shr 2)) and $FF; 150 | end; 151 | if (Idx + 3 <= StrLen) and (Input[Idx + 3] <> '=') then 152 | begin 153 | Inc(NChars); 154 | Result.a[2] := ((DecodeTable[Input[Idx + 2]] shl 6) or DecodeTable[Input[Idx + 3]]) and $FF; 155 | end; 156 | end; 157 | 158 | var 159 | I, J, K: Integer; 160 | Packet: TPacket; 161 | Len: integer; 162 | begin 163 | Result := nil; 164 | SetLength(Result, ((Length(Input) + 2) div 4) * 3); 165 | StrLen := Length(Input); 166 | Len := 0; 167 | J := 0; 168 | for I := 1 to (StrLen + 2) div 4 do 169 | begin 170 | Packet := DecodePacket((I - 1) * 4 + 1, J); 171 | K := 0; 172 | while J > 0 do 173 | begin 174 | Result[Len] := Packet.a[K]; 175 | Inc(Len); 176 | Inc(K); 177 | Dec(J); 178 | end; 179 | end; 180 | SetLength(Result, Len); 181 | end; 182 | 183 | function PercentEncode(const S: string): string; 184 | var 185 | Bytes: TBytes; 186 | B: Byte; 187 | I: integer; 188 | L: integer; 189 | H: string; 190 | begin 191 | Result := ''; 192 | Bytes := TEncoding.UTF8.GetBytes(S); 193 | SetLength(Result, Length(Bytes) * 3); // final string will be maximum 3 times the original bytes 194 | L := 1; 195 | for I := 0 to Length(Bytes) - 1 do 196 | begin 197 | B := Bytes[I]; 198 | 199 | // Check if is unreserved char 200 | if ((B >= 65) and (B <= 90)) // A..Z 201 | or ((B >= 97) and (B <= 122)) // a..z 202 | or ((B >= 48) and (B <= 57)) //0..9 203 | or (B in [45, 46, 95, 126]) // - . _ ~ 204 | or (B in [33, 39, 40, 41, 42]) then // ! ' ( ) * 205 | begin 206 | Result[L] := Chr(B); 207 | Inc(L); 208 | end else 209 | begin 210 | Result[L] := '%'; 211 | H := IntToHex(B, 2); 212 | Result[L + 1] := H[1]; 213 | Result[L + 2] := H[2]; 214 | Inc(L, 3); 215 | end; 216 | end; 217 | SetLength(Result, L - 1); 218 | end; 219 | 220 | procedure AppendQueryParam(var Query: string; const Name, Value: string); 221 | begin 222 | if Query <> '' then 223 | Query := Query + '&'; 224 | Query := Query + PercentEncode(Name) + '=' + PercentEncode(Value); 225 | end; 226 | 227 | function DateToISO(const Value: TDate): string; 228 | var 229 | Year, Month, Day: Word; 230 | begin 231 | DecodeDate(Value, Year, Month, Day); 232 | Result := Format('%.4d-%.2d-%.2d', [Year, Month, Day]); 233 | end; 234 | 235 | function InternalTimeToISO(const Value: TTime; FullISOTimeNotation: Boolean): string; 236 | var 237 | Year, Month, Day, Hour, Minute, Second, MS: Word; 238 | begin 239 | DecodeDateTime(Value, Year, Month, Day, Hour, Minute, Second, MS); 240 | if (MS <> 0) or FullISOTimeNotation then 241 | Result := Format('%.2d:%.2d:%.2d.%.3d', [Hour, Minute, Second, MS]) 242 | else 243 | if Second <> 0 then 244 | Result := Format('%.2d:%.2d:%.2d', [Hour, Minute, Second]) 245 | else 246 | Result := Format('%.2d:%.2d', [Hour, Minute]); 247 | end; 248 | 249 | function TimeToISO(const Value: TTime): string; 250 | begin 251 | Result := InternalTimeToISO(Value, True); 252 | end; 253 | 254 | function InternalDateTimeToISO(const Value: TDateTime; FullNotation: boolean): string; 255 | var 256 | Year, Month, Day, Hour, Minute, Second, MS: Word; 257 | begin 258 | if not FullNotation and (Value = DateOf(Value)) then 259 | Result := DateToISO(Value) 260 | else 261 | begin 262 | DecodeDateTime(Value, Year, Month, Day, Hour, Minute, Second, MS); 263 | if FullNotation or (DateOf(Value) <> 0) then 264 | Result := Format('%sT%s', [DateToISO(Value), TimeToISO(Value)]) 265 | else 266 | Result := Format('%s', [TimeToISO(Value)]) 267 | end; 268 | end; 269 | 270 | function IsDigit(C: Char): Boolean; 271 | begin 272 | {$IFDEF FPC} 273 | Result := TCharacter.IsDigit(C); 274 | {$ELSE} 275 | {$IF CompilerVersion >= 25} 276 | Result := C.IsDigit; 277 | {$ELSE} 278 | Result := Character.IsDigit(C); 279 | {$IFEND} 280 | {$ENDIF} 281 | end; 282 | 283 | function LocalToUTC(const Value: TDateTime): TDateTime; 284 | begin 285 | {$IFDEF FPC} 286 | Result := LocalTimeToUniversal(Value); 287 | {$ELSE} 288 | Result := TTimeZone.Local.ToUniversalTime(Value); 289 | {$ENDIF} 290 | end; 291 | 292 | function UTCToLocal(const Value: TDateTime): TDateTime; 293 | begin 294 | {$IFDEF FPC} 295 | Result := UniversalTimeToLocal(Value); 296 | {$ELSE} 297 | Result := TTimeZone.Local.ToLocalTime(Value); 298 | {$ENDIF} 299 | end; 300 | 301 | function DateTimeToISO(const Value: TDateTime): string; 302 | begin 303 | Result := InternalDateTimeToISO(LocalToUTC(Value), True) + 'Z'; 304 | end; 305 | 306 | function TryISOToDate(const Text: string; out DateTime: TDate): boolean; 307 | var 308 | TextLen: Integer; 309 | 310 | function ExtractDigit(var CharIndex: Integer; out Value: Integer): Boolean; 311 | begin 312 | Result := (CharIndex <= TextLen) and IsDigit(Text[CharIndex]); 313 | if Result then 314 | begin 315 | Value := Ord(Text[CharIndex]) - Ord('0'); 316 | Inc(CharIndex); 317 | end; 318 | end; 319 | 320 | function ExtractDoubleDigit(var CharIndex: Integer; out Value: Integer): Boolean; 321 | var 322 | N10, N: Integer; 323 | begin 324 | Result := ExtractDigit(CharIndex, N10) and ExtractDigit(CharIndex, N); 325 | if Result then 326 | Value := N10 * 10 + N; 327 | end; 328 | 329 | function ExtractFourDigits(var CharIndex: Integer; out Value: Integer): Boolean; 330 | var 331 | N1000, N100, N10, N: Integer; 332 | begin 333 | Result := ExtractDigit(CharIndex, N1000) and ExtractDigit(CharIndex, N100) 334 | and ExtractDigit(CharIndex, N10) and ExtractDigit(CharIndex, N); 335 | if Result then 336 | Value := N1000 * 1000 + N100 * 100 + N10 * 10 + N; 337 | end; 338 | 339 | function ExtractChar(var CharIndex: Integer; C: Char): Boolean; 340 | begin 341 | Result := (CharIndex <= TextLen) and (Text[CharIndex] = C); 342 | if Result then 343 | Inc(CharIndex); 344 | end; 345 | 346 | function IsFinal(var CharIndex: Integer): Boolean; 347 | var 348 | C: Char; 349 | begin 350 | if CharIndex <= TextLen then 351 | C := Text[CharIndex] 352 | else 353 | C := #0; 354 | Result := (C = #0); 355 | end; 356 | 357 | var 358 | Year, Month, Day: Integer; 359 | CharIndex: Integer; 360 | HasDateChar: Boolean; 361 | begin 362 | DateTime := 0; 363 | if Text = '' then 364 | begin 365 | Result := True; 366 | end else 367 | begin 368 | Year := 0; 369 | Month := 0; 370 | 371 | TextLen := Length(Text); 372 | CharIndex := 1; 373 | if not ExtractFourDigits(CharIndex, Year) then Exit(False); 374 | HasDateChar := ExtractChar(CharIndex, '-'); 375 | if not ExtractDoubleDigit(CharIndex, Month) then Exit(False); 376 | if HasDateChar and not ExtractChar(CharIndex, '-') then Exit(False); 377 | if not ExtractDoubleDigit(CharIndex, Day) then Exit(False); 378 | if not IsFinal(CharIndex) then Exit(False); 379 | 380 | DateTime := EncodeDate(Year, Month, Day); 381 | Result := True; 382 | end; 383 | end; 384 | 385 | function InternalISOToTime(const Text: string; 386 | out DateTime: TDateTime; out TimeZone: TTimeZoneInfo): boolean; 387 | var 388 | TextLen: Integer; 389 | 390 | function ExtractDigit(var CharIndex: Integer; out Value: Integer): Boolean; 391 | begin 392 | Result := (CharIndex <= TextLen) and IsDigit(Text[CharIndex]); 393 | if Result then 394 | begin 395 | Value := Ord(Text[CharIndex]) - Ord('0'); 396 | Inc(CharIndex); 397 | end; 398 | end; 399 | 400 | function ExtractDoubleDigit(var CharIndex: Integer; out Value: Integer): Boolean; 401 | var 402 | N10, N: Integer; 403 | begin 404 | Result := ExtractDigit(CharIndex, N10) and ExtractDigit(CharIndex, N); 405 | if Result then 406 | Value := N10 * 10 + N; 407 | end; 408 | 409 | function ExtractTripleDigit(var CharIndex: Integer; out Value: Integer): Boolean; 410 | var 411 | N100, N10, N: Integer; 412 | begin 413 | Result := ExtractDigit(CharIndex, N100) and ExtractDigit(CharIndex, N10) and ExtractDigit(CharIndex, N); 414 | if Result then 415 | Value := N100 * 100 + N10 * 10 + N; 416 | end; 417 | 418 | function ExtractChar(var CharIndex: Integer; C: Char): Boolean; 419 | begin 420 | Result := (CharIndex <= TextLen) and (Text[CharIndex] = C); 421 | if Result then 422 | Inc(CharIndex); 423 | end; 424 | 425 | function ExtractHourMinOffset(var CharIndex: Integer; out HourOff, MinOff: Integer): Boolean; 426 | begin 427 | if not ExtractDoubleDigit(CharIndex, HourOff) then Exit(False); 428 | ExtractChar(CharIndex, ':'); 429 | if not ExtractDoubleDigit(CharIndex, MinOff) then Exit(False); 430 | Result := (CharIndex > TextLen); 431 | end; 432 | 433 | function ExtractTimeZone(var CharIndex: Integer; out HourOff, MinOff: Integer): Boolean; 434 | begin 435 | HourOff := 0; 436 | MinOff := 0; 437 | if ExtractChar(CharIndex, 'Z') then 438 | begin 439 | Result := CharIndex > TextLen; 440 | end 441 | else 442 | if ExtractChar(CharIndex, '+') then 443 | begin 444 | Result := ExtractHourMinOffset(CharIndex, HourOff, MinOff); 445 | end 446 | else 447 | if ExtractChar(CharIndex, '-') then 448 | begin 449 | Result := ExtractHourMinOffset(CharIndex, HourOff, MinOff); 450 | HourOff := -HourOff; 451 | MinOff := -MinOff; 452 | end 453 | else 454 | Result := False; 455 | end; 456 | 457 | function IsFinal(var CharIndex: Integer; out HasTimeZone: Boolean): Boolean; 458 | var 459 | C: Char; 460 | begin 461 | if CharIndex <= TextLen then 462 | C := Text[CharIndex] 463 | else 464 | C := #0; 465 | Result := (C = #0) or (C = 'Z') or (C = '+') or (C = '-'); 466 | HasTimeZone := C <> #0; 467 | end; 468 | 469 | var 470 | Hour, Min, Sec, MSec: Integer; 471 | HourOff, MinOff: Integer; 472 | HasTimeChar: Boolean; 473 | HasTimeZone: Boolean; 474 | CharIndex: Integer; 475 | begin 476 | DateTime := 0; 477 | if Text = '' then 478 | begin 479 | Result := True; 480 | end else 481 | begin 482 | Hour := 0; 483 | Min := 0; 484 | Sec := 0; 485 | MSec := 0; 486 | HourOff := 0; 487 | MinOff := 0; 488 | HasTimeZone := False; 489 | 490 | TextLen := Length(Text); 491 | CharIndex := 1; 492 | if not ExtractDoubleDigit(CharIndex, Hour) then Exit(False); 493 | HasTimeChar := ExtractChar(CharIndex, ':'); 494 | if not ExtractDoubleDigit(CharIndex, Min) then Exit(False); 495 | if not IsFinal(CharIndex, HasTimeZone) then 496 | begin 497 | if HasTimeChar and not ExtractChar(CharIndex, ':') then Exit(False); 498 | if not ExtractDoubleDigit(CharIndex, Sec) then Exit(False); 499 | if not IsFinal(CharIndex, HasTimeZone) then 500 | begin 501 | if not (ExtractChar(CharIndex, '.') and ExtractTripleDigit(CharIndex, MSec) and IsFinal(CharIndex, HasTimeZone)) then 502 | Exit(False); 503 | end; 504 | end; 505 | if HasTimeZone then 506 | begin 507 | if not ExtractTimeZone(CharIndex, HourOff, MinOff) then Exit(False); 508 | end; 509 | 510 | DateTime := EncodeTime(Hour, Min, Sec, MSec); 511 | TimeZone.HourOff := HourOff; 512 | TimeZone.MinOff := MinOff; 513 | TimeZone.HasTimeZone := HasTimeZone; 514 | 515 | Result := True; 516 | end; 517 | end; 518 | 519 | function AdjustTimeZone(var DateTime: TDateTime; const TimeZone: TTimeZoneInfo; 520 | TimeZoneMode: TTimeZoneMode): Boolean; 521 | 522 | function AdjustTime(Value: TDateTime; HourOff, MinOff: Integer): TDateTime; 523 | var 524 | Delta: TDateTime; 525 | begin 526 | Result := Value; 527 | Delta := EncodeTime(Abs(HourOff), Abs(MinOff), 0, 0); 528 | if ((HourOff * MinsPerHour) + MinOff) > 0 then 529 | Result := Result - Delta 530 | else 531 | Result := Result + Delta; 532 | end; 533 | 534 | begin 535 | case TimeZoneMode of 536 | zmError: 537 | // do not accept timezone in the format, otherwise, return local date time 538 | if TimeZone.HasTimeZone then Exit(False); 539 | 540 | zmIgnore: ; 541 | // Return local time ignoring time zone 542 | 543 | zmAsUTC: 544 | if TimeZone.HasTimeZone then 545 | DateTime := AdjustTime(DateTime, TimeZone.HourOff, TimeZone.MinOff) 546 | else 547 | DateTime := LocalToUTC(DateTime); 548 | 549 | zmAsLocal: 550 | if TimeZone.HasTimeZone then 551 | begin 552 | DateTime := AdjustTime(DateTime, TimeZone.HourOff, TimeZone.MinOff); 553 | DateTime := UTCToLocal(DateTime); 554 | end; 555 | end; 556 | Result := True; 557 | end; 558 | 559 | function TryISOToDateTime(const Value: string; out DateTime: TDateTime; TimeZoneMode: TTimeZoneMode): boolean; overload; 560 | var 561 | DatePart: TDate; 562 | TimePart: TDateTime; 563 | TimeZone: TTimeZoneInfo; 564 | TIndex: Integer; 565 | begin 566 | TimeZone := NewTimeZoneInfo; 567 | DateTime := 0; 568 | if Value = '' then 569 | begin 570 | Result := true; 571 | end else 572 | begin 573 | Result := False; 574 | TIndex := Pos('T', Value); 575 | if TIndex > 0 then 576 | begin 577 | Result := TryISOToDate(Copy(Value, 1, TIndex - 1), DatePart) and 578 | InternalISOToTime(Copy(Value, TIndex + 1, MaxInt), TimePart, TimeZone); 579 | if Result then 580 | DateTime := DatePart + TimePart; 581 | end 582 | else 583 | if TryISOToDate(Value, DatePart) then 584 | begin 585 | Result := True; 586 | DateTime := DatePart; 587 | end 588 | else 589 | if InternalISOToTime(Value, TimePart, TimeZone) then 590 | begin 591 | Result := True; 592 | DateTime := TimePart; 593 | end; 594 | if Result then 595 | Result := AdjustTimeZone(DateTime, TimeZone, TimeZoneMode); 596 | end; 597 | end; 598 | 599 | function InternalISOToDateTime(const Value: string; TimeZoneMode: TTimeZoneMode): TDateTime; 600 | begin 601 | if not TryISOToDateTime(Value, Result, TimeZoneMode) then 602 | raise EConvertError.CreateFmt(SInvalidDateFormat, [Value]); 603 | end; 604 | 605 | function ISOToDateTime(const Value: string): TDateTime; 606 | begin 607 | Result := InternalISOTODateTime(Value, zmAsLocal); 608 | end; 609 | 610 | function ISOToDate(const Value: string): TDate; 611 | begin 612 | Result := InternalISOTODateTime(Value, zmAsLocal); 613 | end; 614 | 615 | end. 616 | -------------------------------------------------------------------------------- /UliGpt.deployproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 12 5 | 6 | 7 | 8 | f9613268 9 | 10 | iPhone5 11 | 12 | 13 | 14 | 15 | 16 | UliGpt\ 17 | UliGpt.exe 18 | ProjectOutput 19 | 0 20 | 21 | 22 | True 23 | True 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | UliGpt.app\ 32 | libPCRE.dylib 33 | DependencyModule 34 | 1 35 | 36 | 37 | True 38 | 39 | 40 | UliGpt.app\ 41 | libcgunwind.1.0.dylib 42 | DependencyModule 43 | 1 44 | 45 | 46 | True 47 | 48 | 49 | UliGpt.app\ 50 | libpcre.dylib 51 | DependencyModule 52 | 1 53 | 54 | 55 | True 56 | 57 | 58 | 59 | 60 | UliGpt\res\drawable-xlarge\ 61 | splash_image.png 62 | Android_SplashImage960 63 | 1 64 | 65 | 66 | True 67 | 68 | 69 | UliGpt\res\drawable-xhdpi\ 70 | ic_launcher.png 71 | Android_LauncherIcon96 72 | 1 73 | 74 | 75 | True 76 | 77 | 78 | UliGpt\res\drawable-hdpi\ 79 | ic_launcher.png 80 | Android_LauncherIcon72 81 | 1 82 | 83 | 84 | True 85 | 86 | 87 | UliGpt\res\drawable-large\ 88 | splash_image.png 89 | Android_SplashImage640 90 | 1 91 | 92 | 93 | True 94 | 95 | 96 | UliGpt\library\lib\armeabi-v7a\ 97 | libUliGpt.so 98 | AndroidLibnativeArmeabiv7aFile 99 | 1 100 | 101 | 102 | True 103 | 104 | 105 | UliGpt\res\drawable-normal\ 106 | splash_image.png 107 | Android_SplashImage470 108 | 1 109 | 110 | 111 | True 112 | 113 | 114 | UliGpt\library\lib\mips\ 115 | libUliGpt.so 116 | AndroidLibnativeMipsFile 117 | 1 118 | 119 | 120 | True 121 | 122 | 123 | UliGpt\library\lib\armeabi\ 124 | libUliGpt.so 125 | AndroidLibnativeArmeabiFile 126 | 1 127 | 128 | 129 | True 130 | 131 | 132 | UliGpt\res\drawable-ldpi\ 133 | ic_launcher.png 134 | Android_LauncherIcon36 135 | 1 136 | 137 | 138 | True 139 | 140 | 141 | UliGpt\res\drawable-xxhdpi\ 142 | ic_launcher.png 143 | Android_LauncherIcon144 144 | 1 145 | 146 | 147 | True 148 | 149 | 150 | UliGpt\res\values\ 151 | colors.xml 152 | Android_Colors 153 | 1 154 | 155 | 156 | True 157 | 158 | 159 | UliGpt\ 160 | AndroidManifest.xml 161 | ProjectAndroidManifest 162 | 1 163 | 164 | 165 | True 166 | 167 | 168 | UliGpt\res\values\ 169 | styles.xml 170 | AndroidSplashStyles 171 | 1 172 | 173 | 174 | True 175 | 176 | 177 | UliGpt\res\drawable\ 178 | splash_image_def.xml 179 | AndroidSplashImageDef 180 | 1 181 | 182 | 183 | True 184 | 185 | 186 | UliGpt\res\drawable-mdpi\ 187 | ic_launcher.png 188 | Android_LauncherIcon48 189 | 1 190 | 191 | 192 | True 193 | 194 | 195 | UliGpt\classes\ 196 | classes.dex 197 | AndroidClassesDexFile 198 | 1 199 | 200 | 201 | True 202 | 203 | 204 | UliGpt\res\drawable-small\ 205 | splash_image.png 206 | Android_SplashImage426 207 | 1 208 | 209 | 210 | True 211 | 212 | 213 | UliGpt\library\lib\arm64-v8a\ 214 | libUliGpt.so 215 | ProjectOutput 216 | 1 217 | 218 | 219 | True 220 | True 221 | 222 | 223 | UliGpt\res\values-v21\ 224 | styles.xml 225 | AndroidSplashStylesV21 226 | 1 227 | 228 | 229 | True 230 | 231 | 232 | UliGpt\res\values\ 233 | strings.xml 234 | Android_Strings 235 | 1 236 | 237 | 238 | True 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /UliGpt.dpr: -------------------------------------------------------------------------------- 1 | program UliGpt; 2 | 3 | uses 4 | System.StartUpCopy, 5 | FMX.Forms, 6 | umain in 'umain.pas' {Form1}; 7 | 8 | {$R *.res} 9 | 10 | begin 11 | Application.Initialize; 12 | Application.CreateForm(TForm1, Form1); 13 | Application.Run; 14 | end. 15 | -------------------------------------------------------------------------------- /UliGpt.dproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {B5F43CDE-0239-4873-964D-7230563F7AFF} 4 | 19.0 5 | FMX 6 | UliGpt.dpr 7 | True 8 | Debug 9 | Win32 10 | 37915 11 | Application 12 | 13 | 14 | true 15 | 16 | 17 | true 18 | Base 19 | true 20 | 21 | 22 | true 23 | Base 24 | true 25 | 26 | 27 | true 28 | Base 29 | true 30 | 31 | 32 | true 33 | Base 34 | true 35 | 36 | 37 | true 38 | Base 39 | true 40 | 41 | 42 | true 43 | Base 44 | true 45 | 46 | 47 | true 48 | Base 49 | true 50 | 51 | 52 | true 53 | Base 54 | true 55 | 56 | 57 | true 58 | Cfg_1 59 | true 60 | true 61 | 62 | 63 | true 64 | Cfg_1 65 | true 66 | true 67 | 68 | 69 | true 70 | Base 71 | true 72 | 73 | 74 | true 75 | Cfg_2 76 | true 77 | true 78 | 79 | 80 | true 81 | Cfg_2 82 | true 83 | true 84 | 85 | 86 | .\$(Platform)\$(Config) 87 | .\$(Platform)\$(Config) 88 | false 89 | false 90 | false 91 | false 92 | false 93 | RESTBackendComponents;CloudService;FlexCel_XlsAdapter;soaprtl;soapmidas;RESTComponents;FlexCel_Pdf;emsclientfiredac;DataSnapFireDAC;FireDACIBDriver;emsclient;FireDACCommon;soapserver;FireDACCommonDriver;inet;FireDAC;FireDACSqliteDriver;FlexCel_Core;FlexCel_Render;$(DCC_UsePackage) 94 | System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) 95 | true 96 | true 97 | true 98 | true 99 | true 100 | true 101 | true 102 | true 103 | true 104 | true 105 | $(BDS)\bin\delphi_PROJECTICON.ico 106 | $(BDS)\bin\delphi_PROJECTICNS.icns 107 | UliGpt 108 | 109 | 110 | DBXSqliteDriver;tethering;FireDACDBXDriver;bindengine;DataSnapClient;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FMXTee;DbxCommonDriver;xmlrtl;DataSnapNativeClient;rtl;DbxClientDriver;IndyIPClient;FMX_FlexCel_Components;DataSnapProviderClient;DBXInterBaseDriver;FMX_FlexCel_Core;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;dbexpress;IndyCore;dsnap;DataSnapCommon;IndyIPCommon;ibmonitor;pckHistorico_XE6;ibxpress;ibxbindings;FireDACDSDriver;CustomIPTransport;bindcomp;dbxcds;dsnapxml;dbrtl;IndyProtocols;$(DCC_UsePackage) 111 | package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= 112 | Debug 113 | true 114 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png 115 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png 116 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png 117 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png 118 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png 119 | $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png 120 | $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png 121 | $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png 122 | $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png 123 | android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar 124 | $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png 125 | $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png 126 | $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png 127 | $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png 128 | $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png 129 | 130 | 131 | package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= 132 | Debug 133 | true 134 | true 135 | Base 136 | true 137 | DBXSqliteDriver;tethering;FireDACDBXDriver;bindengine;DataSnapClient;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FMXTee;DbxCommonDriver;xmlrtl;DataSnapNativeClient;rtl;DbxClientDriver;IndyIPClient;FMX_FlexCel_Components;DataSnapProviderClient;DBXInterBaseDriver;FMX_FlexCel_Core;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;dbexpress;IndyCore;dsnap;DataSnapCommon;IndyIPCommon;ibmonitor;pckHistorico_XE6;ibxpress;ibxbindings;FireDACDSDriver;CustomIPTransport;bindcomp;dbxcds;dsnapxml;dbrtl;IndyProtocols;$(DCC_UsePackage);$(DCC_UsePackage) 138 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png 139 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png 140 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png 141 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png 142 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png 143 | $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png 144 | $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png 145 | $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png 146 | $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png 147 | android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar 148 | 149 | 150 | DBXSqliteDriver;tethering;rtcSDK;DISPLUSPACK;FireDACDBXDriver;mxExport_D25;bindengine;DataSnapClient;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FMXTee;DbxCommonDriver;xmlrtl;DataSnapNativeClient;rtl;DbxClientDriver;IndyIPClient;FMX_FlexCel_Components;SqlDir250;DataSnapProviderClient;DBXInterBaseDriver;FMX_FlexCel_Core;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;mxNativeExcel_d12;dbexpress;IndyCore;dsnap;DataSnapCommon;IndyIPCommon;ibmonitor;ibxpress;ibxbindings;FireDACDSDriver;CustomIPTransport;bindcomp;dbxcds;pckUCADOConn;dsnapxml;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage) 151 | CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSFaceIDUsageDescription=The reason for accessing the face id;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers 152 | iPhoneAndiPad 153 | true 154 | Debug 155 | $(MSBuildProjectName) 156 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_57x57.png 157 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png 158 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png 159 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_114x114.png 160 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png 161 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png 162 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_320x480.png 163 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_640x960.png 164 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_640x1136.png 165 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png 166 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png 167 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png 168 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png 169 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png 170 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_29x29.png 171 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png 172 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_58x58.png 173 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png 174 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_72x72.png 175 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png 176 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_144x144.png 177 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png 178 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1004.png 179 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png 180 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x748.png 181 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png 182 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2008.png 183 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png 184 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1496.png 185 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png 186 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png 187 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_50x50.png 188 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png 189 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_100x100.png 190 | $(BDS)\bin\Artwork\iOS\iPad\FM_SettingIcon_29x29.png 191 | $(BDS)\bin\Artwork\iOS\iPad\FM_SettingIcon_58x58.png 192 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png 193 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png 194 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2x.png 195 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_2x.png 196 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_3x.png 197 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_3x.png 198 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImage_2x.png 199 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageDark_2x.png 200 | 201 | 202 | DBXSqliteDriver;tethering;FireDACDBXDriver;bindengine;DataSnapClient;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FMXTee;DbxCommonDriver;xmlrtl;DataSnapNativeClient;rtl;DbxClientDriver;IndyIPClient;FMX_FlexCel_Components;DataSnapProviderClient;DBXInterBaseDriver;FMX_FlexCel_Core;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;dbexpress;IndyCore;dsnap;DataSnapCommon;IndyIPCommon;ibmonitor;ibxpress;ibxbindings;FireDACDSDriver;CustomIPTransport;bindcomp;dbxcds;dsnapxml;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage) 203 | CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSFaceIDUsageDescription=The reason for accessing the face id;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers 204 | iPhoneAndiPad 205 | true 206 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_57x57.png 207 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png 208 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png 209 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_114x114.png 210 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png 211 | $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png 212 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_320x480.png 213 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_640x960.png 214 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_640x1136.png 215 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png 216 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png 217 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png 218 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png 219 | $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png 220 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_29x29.png 221 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png 222 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_58x58.png 223 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png 224 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_72x72.png 225 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png 226 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_144x144.png 227 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png 228 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1004.png 229 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png 230 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x748.png 231 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png 232 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2008.png 233 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png 234 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1496.png 235 | $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png 236 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png 237 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_50x50.png 238 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png 239 | $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_100x100.png 240 | $(BDS)\bin\Artwork\iOS\iPad\FM_SettingIcon_29x29.png 241 | $(BDS)\bin\Artwork\iOS\iPad\FM_SettingIcon_58x58.png 242 | $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png 243 | $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png 244 | 245 | 246 | Debug 247 | true 248 | true 249 | Base 250 | true 251 | DBXSqliteDriver;tethering;FireDACMSSQLDriver;FireDACDBXDriver;bindengine;FireDACMySQLDriver;DataSnapClient;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;emshosting;FireDACTDataDriver;FMXTee;DbxCommonDriver;xmlrtl;DataSnapNativeClient;fmxobj;rtl;DbxClientDriver;DBXSybaseASADriver;IndyIPClient;FMX_FlexCel_Components;FireDACODBCDriver;DataSnapIndy10ServerTransport;DataSnapProviderClient;FireDACMongoDBDriver;DataSnapServerMidas;DBXInterBaseDriver;FMX_FlexCel_Core;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;RadiantShapesFmx;dbexpress;IndyCore;dsnap;DataSnapCommon;FireDACOracleDriver;DBXMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;IndyIPCommon;FireDACPgDriver;ibmonitor;FireDACASADriver;ibxpress;DataSnapServer;ibxbindings;FireDACDSDriver;CustomIPTransport;bindcomp;DBXInformixDriver;dbxcds;dsnapxml;dbrtl;inetdbxpress;IndyProtocols;fmxase;$(DCC_UsePackage);$(DCC_UsePackage) 252 | 253 | 254 | DBXSqliteDriver;dxPSPrVwRibbonRS25;dxRibbonCustomizationFormRS25;DBXDb2Driver;dxSkinOffice2007PinkRS25;tmsdXE10;dxSkinMcSkinRS25;vclactnband;AlphaDBDX10Tokyo;vclFireDAC;cxExportRS25;uSynEdit_R2021;dxHttpIndyRequestRS25;tethering;dxPScxCommonRS25;FireDACADSDriver;cxPivotGridOLAPRS25;JvPluginSystem;rtcSDK;OrpheusDR;FireDACMSSQLDriver;cxSchedulerGridRS25;vcltouch;dxSkinDarkRoomRS25;dxSkinDarkSideRS25;vcldb;JvBands;svn;JvJans;dxGaugeControlRS25;cxLibraryRS25;JvDotNetCtrls;dxSkinOffice2007SilverRS25;VCL_FlexCel_Components;DISPLUSPACK;dxSkinscxSchedulerPainterRS25;dxSkinBlackRS25;vclib;dxSkinOffice2007BlueRS25;dxSkinOffice2007BlackRS25;FireDACDBXDriver;tmsexdXE10;dxSkinVS2010RS25;vclx;dxSkinFoggyRS25;EsVclCore;dxSpreadSheetConditionalFormattingDialogsRS25;dxSkinSevenRS25;dxSkinSpringTimeRS25;dxTileControlRS25;IWBootstrapD102;dxMapControlRS25;dxPDFViewerRS25;dxDockingRS25;mxExport_D25;VCLRESTComponents;rbTCUI1925;dxSkinsdxNavBarPainterRS25;cxPageControlRS25;dxSkinLilianRS25;dxPSLnksRS25;rbFireDAC1925;dxWizardControlRS25;dxRichEditControlRS25;rbRTL1925;TseguriSIGN;vclie;bindengine;dxFireDACServerModeRS25;JvHMI;FireDACMySQLDriver;vquery250;dxSkinOffice2013LightGrayRS25;dxSkinMetropolisRS25;DataSnapClient;dxSkinOffice2016DarkRS25;bindcompdbx;dxSkinBlueprintRS25;dxPSdxLCLnkRS25;DBXSybaseASEDriver;IndyIPServer;dxSkinStardustRS25;Tsignplus;dac250;IndySystem;dxSkinOffice2007GreenRS25;pckFireDacConn_dx10;IconFontsImageListFMX;tmsxlsdXE10;dsnapcon;dxLayoutControlRS25;dxPSRichEditControlLnkRS25;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;PinedaVCL;dxdbtrRS25;Jcl;dxPScxTLLnkRS25;avlocks5dx101;emshosting;dxSpreadSheetRS25;dxSkinVisualStudio2013LightRS25;DBXOdbcDriver;FireDACTDataDriver;FMXTee;cxGridRS25;dxPScxSchedulerLnkRS25;DbxCommonDriver;dxSkinTheAsphaltWorldRS25;dxorgcRS25;rbADO1925;rbIBE1925;dxSkinHighContrastRS25;JvManagedThreads;xmlrtl;DataSnapNativeClient;fmxobj;dxPScxGridLnkRS25;rbUSERDesign1925;rtl;JvTimeFramework;DbxClientDriver;dacvcl250;DBXSybaseASADriver;dxPSCoreRS25;unidacfmx250;rbRest1925;dxmdsRS25;JvSystem;JvStdCtrls;TZXingScan;dxSkinOffice2016ColorfulRS25;appanalytics;rbDAD1925;IndyIPClient;LockBox3DR;bindcompvcl;vcldbx;Tsign;dxSkinscxPCPainterRS25;dxThemeRS25;TeeUI;FMX_FlexCel_Components;rbCIDE1925;SqlDir250;JvDocking;JvPascalInterpreter;VclSmp;BarcodeStudio_EX10;FireDACODBCDriver;JclVcl;DataSnapIndy10ServerTransport;pckUniDacConnDx10;DataSnapProviderClient;dclRBADO1925;FireDACMongoDBDriver;cxVerticalGridRS25;dxtrmdRS25;JvControls;JvPrintPreview;dxADOServerModeRS25;dxSkinPumpkinRS25;Intraweb_15_D10_2;DataSnapServerMidas;dxCoreRS25;cxSchedulerTreeBrowserRS25;dxSkinValentineRS25;DBXInterBaseDriver;BigNumbers;FMX_FlexCel_Core;dxPSTeeChartRS25;svnui;dxSkinOffice2010BlueRS25;dclRBDBE1925;rbRIDE1925;dxSkinMoneyTwinsRS25;JvGlobus;FmxDialogsExt;dxSkinSilverRS25;DBXMSSQLDriver;dxPSdxFCLnkRS25;JvMM;DatasnapConnectorsFreePascal;PngComponentsD;IconFontsImageList;dxSkinOffice2013WhiteRS25;bindcompfmx;JvNet;DBXOracleDriver;unidac250;inetdb;JvAppFrm;CEF4Delphi;RaizeComponentsVcl;dxOfficeCoreRS25;rbTC1925;FmxTeeUI;emsedge;rbIDE1925;fmx;fmxdae;dxPScxPivotGridLnkRS25;dxBarDBNavRS25;dxTabbedMDIRS25;dxSkinBlueRS25;RadiantShapesFmx;RaizeComponentsVclDb;JvWizards;dxSkinDevExpressDarkStyleRS25;EasyListviewD;mxNativeExcel_d12;dbexpress;IndyCore;dxFlowChartRS25;dxSkinsdxDLPainterRS25;OrpheusDBDR;IWBootstrap4D102;JvPageComps;dsnap;DataSnapCommon;dxBarRS25;dxPSDBTeeChartRS25;JvDB;dxSkinLiquidSkyRS25;bdertl;dxdborRS25;DataSnapConnectors;dxPScxExtCommonRS25;acntDX10Tokyo_R;cxPivotGridRS25;dxPSdxSpreadSheetLnkRS25;dxNavBarRS25;rbDB1925;JclDeveloperTools;cxSchedulerRibbonStyleEventEditorRS25;dxSkinCoffeeRS25;FireDACOracleDriver;DBXMySQLDriver;dclRBFireDAC1925;JvCmp;EsVclComponents;DBXFirebirdDriver;FireDACCommonODBC;dxSkinOffice2010SilverRS25;dclRBE1925;cxTreeListRS25;rbRCL1925;dxSkinsdxBarPainterRS25;IndyIPCommon;JvCustom;dxSkinLondonLiquidSkyRS25;vcl;dxPScxVGridLnkRS25;PKIECtrl25;dxBarExtItemsRS25;JvXPCtrls;dxSkinsCoreRS25;dxComnRS25;FireDACDb2Driver;dxPSdxDBTVLnkRS25;dxSkinWhiteprintRS25;dxSkinVisualStudio2013BlueRS25;rbDBE1925;dxSkinGlassOceansRS25;dxSkinMetropolisDarkRS25;dxSkinOffice2013DarkGrayRS25;dxSkinSharpPlusRS25;TeeDB;dxSkinCaramelRS25;dxServerModeRS25;dxPScxPCProdRS25;dclRBIBE1925;JvCore;JvCrypt;FireDACPgDriver;ibmonitor;FireDACASADriver;cxEditorsRS25;dxSkiniMaginaryRS25;pckHistorico_XE6;TMSFMXPackPkgDXE102;ChromeTabs_R;JvDlgs;JvRuntimeDesign;rbUSER1925;rbDIDE1925;ibxpress;Tee;TMSCloudPkgDXE11;DataSnapServer;ibxbindings;dxPsPrVwAdvRS25;vclwinx;FireDACDSDriver;dxSkinSevenClassicRS25;cxDataRS25;cxTreeListdxBarPopupMenuRS25;CustomIPTransport;vcldsnap;dxPSdxOCLnkRS25;dxSkinSharpRS25;rbRAP1925;bindcomp;DBXInformixDriver;tmswizdXE10;cxPivotGridChartRS25;cxSchedulerRS25;dxBarExtDBItemsRS25;dxSkinVisualStudio2013DarkRS25;dxSkinOffice2010BlackRS25;rbBDE1925;dxDBXServerModeRS25;dxSkinDevExpressStyleRS25;dxGDIPlusRS25;dxPSdxGaugeControlLnkRS25;dxSkinsdxRibbonPainterRS25;crcontrols250;PKIEDB25;dbxcds;VCL_FlexCel_Core;unidacvcl250;adortl;pckUCADOConn;RadiantShapesFmx_Design;dacfmx250;dxPSdxDBOCLnkRS25;MPCommonLibD;dxRibbonRS25;dsnapxml;dxSpellCheckerRS25;dbrtl;inetdbxpress;IndyProtocols;BarcodeReportBuilder_EX10;dxSkinSummer2008RS25;dxPSdxMapControlLnkRS25;dxSkinXmas2008BlueRS25;fmxase;$(DCC_UsePackage) 255 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) 256 | Debug 257 | true 258 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= 259 | 1033 260 | $(BDS)\bin\default_app.manifest 261 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png 262 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png 263 | 264 | 265 | DBXSqliteDriver;dxPSPrVwRibbonRS25;dxRibbonCustomizationFormRS25;DBXDb2Driver;dxSkinOffice2007PinkRS25;tmsdXE10;dxSkinMcSkinRS25;vclactnband;vclFireDAC;cxExportRS25;uSynEdit_R2021;dxHttpIndyRequestRS25;tethering;dxPScxCommonRS25;FireDACADSDriver;cxPivotGridOLAPRS25;OrpheusDR;FireDACMSSQLDriver;cxSchedulerGridRS25;vcltouch;dxSkinDarkRoomRS25;dxSkinDarkSideRS25;vcldb;dxGaugeControlRS25;cxLibraryRS25;dxSkinOffice2007SilverRS25;VCL_FlexCel_Components;dxSkinscxSchedulerPainterRS25;dxSkinBlackRS25;vclib;dxSkinOffice2007BlueRS25;dxSkinOffice2007BlackRS25;FireDACDBXDriver;tmsexdXE10;dxSkinVS2010RS25;vclx;dxSkinFoggyRS25;EsVclCore;dxSpreadSheetConditionalFormattingDialogsRS25;dxSkinSevenRS25;dxSkinSpringTimeRS25;dxTileControlRS25;dxMapControlRS25;dxPDFViewerRS25;dxDockingRS25;VCLRESTComponents;dxSkinsdxNavBarPainterRS25;cxPageControlRS25;dxSkinLilianRS25;dxPSLnksRS25;dxWizardControlRS25;dxRichEditControlRS25;vclie;bindengine;dxFireDACServerModeRS25;FireDACMySQLDriver;dxSkinOffice2013LightGrayRS25;dxSkinMetropolisRS25;DataSnapClient;dxSkinOffice2016DarkRS25;bindcompdbx;dxSkinBlueprintRS25;dxPSdxLCLnkRS25;DBXSybaseASEDriver;IndyIPServer;dxSkinStardustRS25;IndySystem;dxSkinOffice2007GreenRS25;pckFireDacConn_dx10;dsnapcon;dxLayoutControlRS25;dxPSRichEditControlLnkRS25;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;PinedaVCL;dxdbtrRS25;dxPScxTLLnkRS25;avlocks5dx101;emshosting;dxSpreadSheetRS25;dxSkinVisualStudio2013LightRS25;DBXOdbcDriver;FireDACTDataDriver;FMXTee;cxGridRS25;dxPScxSchedulerLnkRS25;DbxCommonDriver;dxSkinTheAsphaltWorldRS25;dxorgcRS25;dxSkinHighContrastRS25;xmlrtl;DataSnapNativeClient;fmxobj;dxPScxGridLnkRS25;rtl;DbxClientDriver;DBXSybaseASADriver;dxPSCoreRS25;dxmdsRS25;dxSkinOffice2016ColorfulRS25;appanalytics;IndyIPClient;LockBox3DR;bindcompvcl;dxSkinscxPCPainterRS25;dxThemeRS25;TeeUI;FMX_FlexCel_Components;VclSmp;BarcodeStudio_EX10;FireDACODBCDriver;DataSnapIndy10ServerTransport;DataSnapProviderClient;FireDACMongoDBDriver;cxVerticalGridRS25;dxtrmdRS25;dxADOServerModeRS25;dxSkinPumpkinRS25;DataSnapServerMidas;dxCoreRS25;cxSchedulerTreeBrowserRS25;dxSkinValentineRS25;DBXInterBaseDriver;FMX_FlexCel_Core;dxPSTeeChartRS25;dxSkinOffice2010BlueRS25;dxSkinMoneyTwinsRS25;dxSkinSilverRS25;DBXMSSQLDriver;dxPSdxFCLnkRS25;DatasnapConnectorsFreePascal;PngComponentsD;dxSkinOffice2013WhiteRS25;bindcompfmx;DBXOracleDriver;inetdb;CEF4Delphi;RaizeComponentsVcl;dxOfficeCoreRS25;FmxTeeUI;emsedge;fmx;fmxdae;dxPScxPivotGridLnkRS25;dxBarDBNavRS25;dxTabbedMDIRS25;dxSkinBlueRS25;RadiantShapesFmx;RaizeComponentsVclDb;dxSkinDevExpressDarkStyleRS25;EasyListviewD;dbexpress;IndyCore;dxFlowChartRS25;dxSkinsdxDLPainterRS25;OrpheusDBDR;dsnap;DataSnapCommon;dxBarRS25;dxPSDBTeeChartRS25;dxSkinLiquidSkyRS25;dxdborRS25;DataSnapConnectors;dxPScxExtCommonRS25;cxPivotGridRS25;dxPSdxSpreadSheetLnkRS25;dxNavBarRS25;cxSchedulerRibbonStyleEventEditorRS25;dxSkinCoffeeRS25;FireDACOracleDriver;DBXMySQLDriver;EsVclComponents;DBXFirebirdDriver;FireDACCommonODBC;dxSkinOffice2010SilverRS25;cxTreeListRS25;dxSkinsdxBarPainterRS25;IndyIPCommon;dxSkinLondonLiquidSkyRS25;vcl;dxPScxVGridLnkRS25;PKIECtrl25;dxBarExtItemsRS25;dxSkinsCoreRS25;dxComnRS25;FireDACDb2Driver;dxPSdxDBTVLnkRS25;dxSkinWhiteprintRS25;dxSkinVisualStudio2013BlueRS25;dxSkinGlassOceansRS25;dxSkinMetropolisDarkRS25;dxSkinOffice2013DarkGrayRS25;dxSkinSharpPlusRS25;TeeDB;dxSkinCaramelRS25;dxServerModeRS25;dxPScxPCProdRS25;FireDACPgDriver;ibmonitor;FireDACASADriver;cxEditorsRS25;dxSkiniMaginaryRS25;ChromeTabs_R;ibxpress;Tee;DataSnapServer;ibxbindings;dxPsPrVwAdvRS25;vclwinx;FireDACDSDriver;dxSkinSevenClassicRS25;cxDataRS25;cxTreeListdxBarPopupMenuRS25;CustomIPTransport;vcldsnap;dxPSdxOCLnkRS25;dxSkinSharpRS25;bindcomp;DBXInformixDriver;cxPivotGridChartRS25;cxSchedulerRS25;dxBarExtDBItemsRS25;dxSkinVisualStudio2013DarkRS25;dxSkinOffice2010BlackRS25;dxDBXServerModeRS25;dxSkinDevExpressStyleRS25;dxGDIPlusRS25;dxPSdxGaugeControlLnkRS25;dxSkinsdxRibbonPainterRS25;PKIEDB25;dbxcds;VCL_FlexCel_Core;adortl;dxPSdxDBOCLnkRS25;MPCommonLibD;dxRibbonRS25;dsnapxml;dxSpellCheckerRS25;dbrtl;inetdbxpress;IndyProtocols;BarcodeReportBuilder_EX10;dxSkinSummer2008RS25;dxPSdxMapControlLnkRS25;dxSkinXmas2008BlueRS25;fmxase;$(DCC_UsePackage) 266 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) 267 | Debug 268 | true 269 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= 270 | 1033 271 | $(BDS)\bin\default_app.manifest 272 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png 273 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png 274 | 275 | 276 | DEBUG;$(DCC_Define) 277 | true 278 | false 279 | true 280 | true 281 | true 282 | 283 | 284 | false 285 | true 286 | PerMonitor 287 | 288 | 289 | true 290 | PerMonitor 291 | 292 | 293 | false 294 | RELEASE;$(DCC_Define) 295 | 0 296 | 0 297 | 298 | 299 | true 300 | PerMonitor 301 | 302 | 303 | true 304 | PerMonitor 305 | 306 | 307 | 308 | MainSource 309 | 310 | 311 |
Form1
312 | fmx 313 |
314 | 315 | Cfg_2 316 | Base 317 | 318 | 319 | Base 320 | 321 | 322 | Cfg_1 323 | Base 324 | 325 |
326 | 327 | Delphi.Personality.12 328 | Application 329 | 330 | 331 | 332 | UliGpt.dpr 333 | 334 | 335 | 336 | 337 | 338 | true 339 | 340 | 341 | 342 | 343 | true 344 | 345 | 346 | 347 | 348 | true 349 | 350 | 351 | 352 | 353 | true 354 | 355 | 356 | 357 | 358 | UliGpt.exe 359 | true 360 | 361 | 362 | 363 | 364 | true 365 | 366 | 367 | 368 | 369 | 1 370 | 371 | 372 | Contents\MacOS 373 | 1 374 | 375 | 376 | 0 377 | 378 | 379 | 380 | 381 | classes 382 | 1 383 | 384 | 385 | classes 386 | 1 387 | 388 | 389 | 390 | 391 | res\xml 392 | 1 393 | 394 | 395 | res\xml 396 | 1 397 | 398 | 399 | 400 | 401 | library\lib\armeabi-v7a 402 | 1 403 | 404 | 405 | 406 | 407 | library\lib\armeabi 408 | 1 409 | 410 | 411 | library\lib\armeabi 412 | 1 413 | 414 | 415 | 416 | 417 | library\lib\armeabi-v7a 418 | 1 419 | 420 | 421 | 422 | 423 | library\lib\mips 424 | 1 425 | 426 | 427 | library\lib\mips 428 | 1 429 | 430 | 431 | 432 | 433 | library\lib\armeabi-v7a 434 | 1 435 | 436 | 437 | library\lib\arm64-v8a 438 | 1 439 | 440 | 441 | 442 | 443 | library\lib\armeabi-v7a 444 | 1 445 | 446 | 447 | 448 | 449 | res\drawable 450 | 1 451 | 452 | 453 | res\drawable 454 | 1 455 | 456 | 457 | 458 | 459 | res\values 460 | 1 461 | 462 | 463 | res\values 464 | 1 465 | 466 | 467 | 468 | 469 | res\values-v21 470 | 1 471 | 472 | 473 | res\values-v21 474 | 1 475 | 476 | 477 | 478 | 479 | res\values 480 | 1 481 | 482 | 483 | res\values 484 | 1 485 | 486 | 487 | 488 | 489 | res\drawable 490 | 1 491 | 492 | 493 | res\drawable 494 | 1 495 | 496 | 497 | 498 | 499 | res\drawable-xxhdpi 500 | 1 501 | 502 | 503 | res\drawable-xxhdpi 504 | 1 505 | 506 | 507 | 508 | 509 | res\drawable-ldpi 510 | 1 511 | 512 | 513 | res\drawable-ldpi 514 | 1 515 | 516 | 517 | 518 | 519 | res\drawable-mdpi 520 | 1 521 | 522 | 523 | res\drawable-mdpi 524 | 1 525 | 526 | 527 | 528 | 529 | res\drawable-hdpi 530 | 1 531 | 532 | 533 | res\drawable-hdpi 534 | 1 535 | 536 | 537 | 538 | 539 | res\drawable-xhdpi 540 | 1 541 | 542 | 543 | res\drawable-xhdpi 544 | 1 545 | 546 | 547 | 548 | 549 | res\drawable-mdpi 550 | 1 551 | 552 | 553 | res\drawable-mdpi 554 | 1 555 | 556 | 557 | 558 | 559 | res\drawable-hdpi 560 | 1 561 | 562 | 563 | res\drawable-hdpi 564 | 1 565 | 566 | 567 | 568 | 569 | res\drawable-xhdpi 570 | 1 571 | 572 | 573 | res\drawable-xhdpi 574 | 1 575 | 576 | 577 | 578 | 579 | res\drawable-xxhdpi 580 | 1 581 | 582 | 583 | res\drawable-xxhdpi 584 | 1 585 | 586 | 587 | 588 | 589 | res\drawable-xxxhdpi 590 | 1 591 | 592 | 593 | res\drawable-xxxhdpi 594 | 1 595 | 596 | 597 | 598 | 599 | res\drawable-small 600 | 1 601 | 602 | 603 | res\drawable-small 604 | 1 605 | 606 | 607 | 608 | 609 | res\drawable-normal 610 | 1 611 | 612 | 613 | res\drawable-normal 614 | 1 615 | 616 | 617 | 618 | 619 | res\drawable-large 620 | 1 621 | 622 | 623 | res\drawable-large 624 | 1 625 | 626 | 627 | 628 | 629 | res\drawable-xlarge 630 | 1 631 | 632 | 633 | res\drawable-xlarge 634 | 1 635 | 636 | 637 | 638 | 639 | res\values 640 | 1 641 | 642 | 643 | res\values 644 | 1 645 | 646 | 647 | 648 | 649 | 1 650 | 651 | 652 | Contents\MacOS 653 | 1 654 | 655 | 656 | 0 657 | 658 | 659 | 660 | 661 | Contents\MacOS 662 | 1 663 | .framework 664 | 665 | 666 | Contents\MacOS 667 | 1 668 | .framework 669 | 670 | 671 | 0 672 | 673 | 674 | 675 | 676 | 1 677 | .dylib 678 | 679 | 680 | 1 681 | .dylib 682 | 683 | 684 | 1 685 | .dylib 686 | 687 | 688 | Contents\MacOS 689 | 1 690 | .dylib 691 | 692 | 693 | Contents\MacOS 694 | 1 695 | .dylib 696 | 697 | 698 | 0 699 | .dll;.bpl 700 | 701 | 702 | 703 | 704 | 1 705 | .dylib 706 | 707 | 708 | 1 709 | .dylib 710 | 711 | 712 | 1 713 | .dylib 714 | 715 | 716 | Contents\MacOS 717 | 1 718 | .dylib 719 | 720 | 721 | Contents\MacOS 722 | 1 723 | .dylib 724 | 725 | 726 | 0 727 | .bpl 728 | 729 | 730 | 731 | 732 | 0 733 | 734 | 735 | 0 736 | 737 | 738 | 0 739 | 740 | 741 | 0 742 | 743 | 744 | 0 745 | 746 | 747 | Contents\Resources\StartUp\ 748 | 0 749 | 750 | 751 | Contents\Resources\StartUp\ 752 | 0 753 | 754 | 755 | 0 756 | 757 | 758 | 759 | 760 | 1 761 | 762 | 763 | 1 764 | 765 | 766 | 1 767 | 768 | 769 | 770 | 771 | 1 772 | 773 | 774 | 1 775 | 776 | 777 | 1 778 | 779 | 780 | 781 | 782 | 1 783 | 784 | 785 | 1 786 | 787 | 788 | 1 789 | 790 | 791 | 792 | 793 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 794 | 1 795 | 796 | 797 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 798 | 1 799 | 800 | 801 | 802 | 803 | 1 804 | 805 | 806 | 1 807 | 808 | 809 | 1 810 | 811 | 812 | 813 | 814 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 815 | 1 816 | 817 | 818 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 819 | 1 820 | 821 | 822 | 823 | 824 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 825 | 1 826 | 827 | 828 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 829 | 1 830 | 831 | 832 | 833 | 834 | 1 835 | 836 | 837 | 1 838 | 839 | 840 | 1 841 | 842 | 843 | 844 | 845 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 846 | 1 847 | 848 | 849 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 850 | 1 851 | 852 | 853 | 854 | 855 | 1 856 | 857 | 858 | 1 859 | 860 | 861 | 1 862 | 863 | 864 | 865 | 866 | 1 867 | 868 | 869 | 1 870 | 871 | 872 | 1 873 | 874 | 875 | 876 | 877 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 878 | 1 879 | 880 | 881 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 882 | 1 883 | 884 | 885 | 886 | 887 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 888 | 1 889 | 890 | 891 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 892 | 1 893 | 894 | 895 | 896 | 897 | 1 898 | 899 | 900 | 1 901 | 902 | 903 | 904 | 905 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 906 | 1 907 | 908 | 909 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 910 | 1 911 | 912 | 913 | 914 | 915 | 1 916 | 917 | 918 | 1 919 | 920 | 921 | 922 | 923 | ..\ 924 | 1 925 | 926 | 927 | ..\ 928 | 1 929 | 930 | 931 | 932 | 933 | 1 934 | 935 | 936 | 1 937 | 938 | 939 | 1 940 | 941 | 942 | 943 | 944 | ..\$(PROJECTNAME).launchscreen 945 | 64 946 | 947 | 948 | ..\$(PROJECTNAME).launchscreen 949 | 64 950 | 951 | 952 | 953 | 954 | 1 955 | 956 | 957 | 1 958 | 959 | 960 | 1 961 | 962 | 963 | 964 | 965 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 966 | 1 967 | 968 | 969 | 970 | 971 | ..\ 972 | 1 973 | 974 | 975 | ..\ 976 | 1 977 | 978 | 979 | 980 | 981 | Contents 982 | 1 983 | 984 | 985 | Contents 986 | 1 987 | 988 | 989 | 990 | 991 | Contents\Resources 992 | 1 993 | 994 | 995 | Contents\Resources 996 | 1 997 | 998 | 999 | 1000 | 1001 | library\lib\armeabi-v7a 1002 | 1 1003 | 1004 | 1005 | library\lib\arm64-v8a 1006 | 1 1007 | 1008 | 1009 | 1 1010 | 1011 | 1012 | 1 1013 | 1014 | 1015 | 1 1016 | 1017 | 1018 | 1 1019 | 1020 | 1021 | Contents\MacOS 1022 | 1 1023 | 1024 | 1025 | Contents\MacOS 1026 | 1 1027 | 1028 | 1029 | 0 1030 | 1031 | 1032 | 1033 | 1034 | library\lib\armeabi-v7a 1035 | 1 1036 | 1037 | 1038 | 1039 | 1040 | 1 1041 | 1042 | 1043 | 1 1044 | 1045 | 1046 | 1047 | 1048 | Assets 1049 | 1 1050 | 1051 | 1052 | Assets 1053 | 1 1054 | 1055 | 1056 | 1057 | 1058 | Assets 1059 | 1 1060 | 1061 | 1062 | Assets 1063 | 1 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | True 1079 | True 1080 | True 1081 | True 1082 | False 1083 | True 1084 | True 1085 | True 1086 | 1087 | 1088 | 12 1089 | 1090 | 1091 | 1092 | 1093 |
1094 | -------------------------------------------------------------------------------- /UliGpt.dproj.local: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 2022/06/06 14:58:07.000.386,=C:\Users\Usuario\Documents\Embarcadero\Studio\Projects\GroupedItems2.pas 5 | 2022/06/06 14:58:08.000.105,=C:\Users\Usuario\Documents\Embarcadero\Studio\Projects\SplitItemDetail1.pas 6 | 2023/01/24 16:12:13.000.926,=C:\Users\Usuario\Documents\Embarcadero\Studio\Projects\Unit1.pas 7 | 2023/01/24 16:14:18.000.674,C:\Users\Usuario\Documents\Embarcadero\Studio\Projects\pruebas\delphiAI\umain.fmx=C:\Users\Usuario\Documents\Embarcadero\Studio\Projects\Unit1.fmx 8 | 2023/01/24 16:14:18.000.674,C:\Users\Usuario\Documents\Embarcadero\Studio\Projects\pruebas\delphiAI\umain.pas=C:\Users\Usuario\Documents\Embarcadero\Studio\Projects\Unit1.pas 9 | 2023/01/24 16:14:26.000.648,C:\Users\Usuario\Documents\Embarcadero\Studio\Projects\pruebas\delphiAI\ChatGpt.dproj=C:\Users\Usuario\Documents\Embarcadero\Studio\Projects\Project1.dproj 10 | 2023/01/24 16:15:34.000.113,C:\Users\Usuario\Documents\Embarcadero\Studio\Projects\pruebas\delphiAI\UliGpt.dproj=C:\Users\Usuario\Documents\Embarcadero\Studio\Projects\pruebas\delphiAI\ChatGpt.dproj 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /UliGpt.identcache: -------------------------------------------------------------------------------- 1 | ,C:\Users\sidney\Documents\delphiAI\umain.pas-C:\Users\sidney\Documents\delphiAI\UliGpt.dpr -------------------------------------------------------------------------------- /UliGpt.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/UliGpt.res -------------------------------------------------------------------------------- /UliGpt.skincfg: -------------------------------------------------------------------------------- 1 | [ExpressSkins] 2 | Default=1 3 | ShowNotifications=1 4 | Enabled=1 5 | dxSkinBlack=1 6 | dxSkinBlue=1 7 | dxSkinBlueprint=1 8 | dxSkinCaramel=1 9 | dxSkinCoffee=1 10 | dxSkinDarkRoom=1 11 | dxSkinDarkSide=1 12 | dxSkinDevExpressDarkStyle=1 13 | dxSkinDevExpressStyle=1 14 | dxSkinFoggy=1 15 | dxSkinGlassOceans=1 16 | dxSkinHighContrast=1 17 | dxSkiniMaginary=1 18 | dxSkinLilian=1 19 | dxSkinLiquidSky=1 20 | dxSkinLondonLiquidSky=1 21 | dxSkinMcSkin=1 22 | dxSkinMetropolis=1 23 | dxSkinMetropolisDark=1 24 | dxSkinMoneyTwins=1 25 | dxSkinOffice2007Black=1 26 | dxSkinOffice2007Blue=1 27 | dxSkinOffice2007Green=1 28 | dxSkinOffice2007Pink=1 29 | dxSkinOffice2007Silver=1 30 | dxSkinOffice2010Black=1 31 | dxSkinOffice2010Blue=1 32 | dxSkinOffice2010Silver=1 33 | dxSkinOffice2013DarkGray=1 34 | dxSkinOffice2013LightGray=1 35 | dxSkinOffice2013White=1 36 | dxSkinOffice2016Colorful=1 37 | dxSkinOffice2016Dark=1 38 | dxSkinPumpkin=1 39 | dxSkinSeven=1 40 | dxSkinSevenClassic=1 41 | dxSkinSharp=1 42 | dxSkinSharpPlus=1 43 | dxSkinSilver=1 44 | dxSkinSpringTime=1 45 | dxSkinStardust=1 46 | dxSkinSummer2008=1 47 | dxSkinTheAsphaltWorld=1 48 | dxSkinsDefaultPainters=1 49 | dxSkinValentine=1 50 | dxSkinVisualStudio2013Blue=1 51 | dxSkinVisualStudio2013Dark=1 52 | dxSkinVisualStudio2013Light=1 53 | dxSkinVS2010=1 54 | dxSkinWhiteprint=1 55 | dxSkinXmas2008Blue=1 56 | -------------------------------------------------------------------------------- /Win32/Debug/OpenAIClient.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Win32/Debug/OpenAIClient.dcu -------------------------------------------------------------------------------- /Win32/Debug/OpenAIDtos.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Win32/Debug/OpenAIDtos.dcu -------------------------------------------------------------------------------- /Win32/Debug/OpenAIJson.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Win32/Debug/OpenAIJson.dcu -------------------------------------------------------------------------------- /Win32/Debug/OpenApiHttp.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Win32/Debug/OpenApiHttp.dcu -------------------------------------------------------------------------------- /Win32/Debug/OpenApiJson.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Win32/Debug/OpenApiJson.dcu -------------------------------------------------------------------------------- /Win32/Debug/OpenApiRest.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Win32/Debug/OpenApiRest.dcu -------------------------------------------------------------------------------- /Win32/Debug/OpenApiUtils.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Win32/Debug/OpenApiUtils.dcu -------------------------------------------------------------------------------- /Win32/Debug/umain.dcu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/displus/UlichatOpenAI/91240ba19ad5f149e1bc1600b64de4a714f28a49/Win32/Debug/umain.dcu -------------------------------------------------------------------------------- /umain.pas: -------------------------------------------------------------------------------- 1 | unit umain; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, 7 | FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, 8 | OpenAIClient, 9 | OpenAIDtos, FMX.Layouts, FMX.ListBox, FMX.StdCtrls, FMX.Controls.Presentation, 10 | FMX.ScrollBox, FMX.Memo, FMX.Objects, FMX.Ani, FMX.Edit, 11 | IdURI, FMX.Memo.Types, 12 | {$IFDEF MSWINDOWS} 13 | Winapi.ShellAPI, Winapi.Windows; 14 | {$ELSE} 15 | {$IFDEF ANDROID} 16 | Androidapi.Helpers, 17 | FMX.Helpers.Android, Androidapi.JNI.GraphicsContentViewText, 18 | Androidapi.JNI.Net, Androidapi.JNI.JavaTypes; 19 | {$ELSE} 20 | {$IFDEF IOS} 21 | Macapi.Helpers, iOSapi.Foundation, FMX.Helpers.iOS; 22 | {$ENDIF IOS} 23 | {$ENDIF ANDROID} 24 | {$ENDIF MSWINDOWS} 25 | type 26 | TForm1 = class(TForm) 27 | ScaledLayout1: TScaledLayout; 28 | Layout2: TLayout; 29 | Layout1: TLayout; 30 | Image1: TImage; 31 | StyleBook1: TStyleBook; 32 | Layout3: TLayout; 33 | Layout4: TLayout; 34 | Layout5: TLayout; 35 | Layout6: TLayout; 36 | edquestion: TMemo; 37 | bQuest: TButton; 38 | lbAnswer: TListBox; 39 | Layout7: TLayout; 40 | Layout8: TLayout; 41 | Image2: TImage; 42 | fpemby: TFloatAnimation; 43 | Label1: TLabel; 44 | edApikey: TEdit; 45 | Memo1: TMemo; 46 | lWrite: TLabel; 47 | lresponse: TLabel; 48 | btLink: TButton; 49 | Layout9: TLayout; 50 | Label4: TLabel; 51 | Image3: TImage; 52 | Image4: TImage; 53 | Image5: TImage; 54 | procedure bQuestClick(Sender: TObject); 55 | procedure FormShow(Sender: TObject); 56 | procedure edquestionEnter(Sender: TObject); 57 | procedure btLinkClick(Sender: TObject); 58 | procedure edApikeyClick(Sender: TObject); 59 | procedure Image3Click(Sender: TObject); 60 | procedure Image4Click(Sender: TObject); 61 | procedure Image5Click(Sender: TObject); 62 | private 63 | procedure setleng(lenguage:integer); 64 | { Private declarations } 65 | public 66 | item:integer; 67 | function AskQuestion(const Question: string): string;{ Public declarations } 68 | function OpenURL(const URL: string; const DisplayError: Boolean = False): Boolean; 69 | 70 | end; 71 | 72 | const 73 | CApiKeyVar = 'OPENAI_API_KEY'; 74 | 75 | var 76 | Client: IOpenAIClient; 77 | Form1: TForm1; 78 | 79 | implementation 80 | 81 | {$R *.fmx} 82 | procedure TForm1.setleng(lenguage:integer); 83 | begin 84 | case lenguage of 85 | 1:begin 86 | lresponse.text := 'Odpowiedzi:'; 87 | lwrite.text := 'Pytanie:'; 88 | bquest.text := 'Zapytać'; 89 | end; 90 | 2:begin 91 | lwrite.text := 'Escribe tu pregunta :'; 92 | lresponse.text := 'Respuestas'; 93 | bquest.text := 'Preguntar'; 94 | end; 95 | 3:begin 96 | lresponse.text := 'REsponses:'; 97 | lwrite.text := 'Write your Question:'; 98 | bquest.text := 'Ask Question'; 99 | end; 100 | 101 | end 102 | end; 103 | 104 | function TForm1.AskQuestion(const Question: string): string; 105 | var 106 | Request: TCreateCompletionRequest; 107 | Response: TCreateCompletionResponse; 108 | begin 109 | Response := nil; 110 | Request := TCreateCompletionRequest.Create; 111 | try 112 | Request.Prompt := Question; 113 | Request.Model := 'text-davinci-003'; 114 | Request.MaxTokens := 2048; // Be careful as this can quickly consume your API quota. 115 | Response := Client.OpenAI.CreateCompletion(Request); 116 | if Assigned(Response.Choices) and (Response.Choices.Count > 0) then 117 | Result := Response.Choices[0].Text 118 | else 119 | Result := ''; 120 | finally 121 | Request.Free; 122 | Response.Free; 123 | end; 124 | 125 | end; 126 | 127 | procedure TForm1.btLinkClick(Sender: TObject); 128 | begin 129 | openurl( 'https://beta.openai.com/account/api-keys'); 130 | end; 131 | 132 | procedure TForm1.bQuestClick(Sender: TObject); 133 | var 134 | Question: string; 135 | Answer: string; 136 | aItem: TListBoxItem; 137 | begin 138 | Client.Config.AccessToken := edApikey.Text; 139 | fpemby.Enabled:=true; 140 | Question:=edQuestion.Text; 141 | Answer := AskQuestion(Question); 142 | 143 | // aItem := TListBoxItem.Create(Self); 144 | // aItem.Tag := item+1; 145 | 146 | if Answer <> '' then 147 | memo1.lines.add(Answer) 148 | else 149 | MEMO1.Text := 'Could not retrieve an answer.'; 150 | 151 | // aItem.Parent := MEMO1; 152 | 153 | fpemby.Enabled:=false; 154 | end; 155 | 156 | procedure TForm1.edApikeyClick(Sender: TObject); 157 | begin 158 | edapikey.Password:=not edapikey.Password; 159 | end; 160 | 161 | procedure TForm1.edquestionEnter(Sender: TObject); 162 | begin 163 | 164 | fpemby.Enabled:=true; 165 | end; 166 | 167 | procedure TForm1.FormShow(Sender: TObject); 168 | begin 169 | Client := TOpenAIClient.Create; 170 | item:=1; 171 | end; 172 | 173 | procedure TForm1.Image3Click(Sender: TObject); 174 | begin 175 | setLeng(1); 176 | 177 | end; 178 | 179 | procedure TForm1.Image4Click(Sender: TObject); 180 | begin 181 | setLeng(2); 182 | end; 183 | 184 | procedure TForm1.Image5Click(Sender: TObject); 185 | begin 186 | setLeng(3); 187 | end; 188 | 189 | function TForm1.OpenURL(const URL: string; 190 | const DisplayError: Boolean): Boolean; 191 | {$IFDEF MSWINDOWS} 192 | begin 193 | ShellExecute(0, 'OPEN', PChar(URL), '', '', SW_SHOWNORMAL); 194 | end; 195 | 196 | 197 | 198 | {$ELSE} 199 | 200 | 201 | {$IFDEF ANDROID} 202 | var 203 | Intent: JIntent; 204 | begin 205 | // There may be an issue with the geo: prefix and URLEncode. 206 | // will need to research 207 | Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW, 208 | TJnet_Uri.JavaClass.parse(StringToJString(TIdURI.URLEncode(URL)))); 209 | try 210 | SharedActivity.startActivity(Intent); 211 | exit(true); 212 | except 213 | on e: Exception do 214 | begin 215 | if DisplayError then ShowMessage('Error: ' + e.Message); 216 | exit(false); 217 | end; 218 | end; 219 | end; 220 | 221 | 222 | 223 | {$ELSE} 224 | {$IFDEF IOS} 225 | var 226 | NSU: NSUrl; 227 | begin 228 | // iOS doesn't like spaces, so URL encode is important. 229 | NSU := StrToNSUrl(TIdURI.URLEncode(URL)); 230 | if SharedApplication.canOpenURL(NSU) then 231 | exit(SharedApplication.openUrl(NSU)) 232 | else 233 | begin 234 | if DisplayError then 235 | ShowMessage('Error: Opening "' + URL + '" not supported.'); 236 | exit(false); 237 | end; 238 | end; 239 | 240 | {$ELSE} 241 | begin 242 | raise Exception.Create('Not supported!'); 243 | end; 244 | 245 | {$ENDIF IOS} 246 | {$ENDIF ANDROID} 247 | {$ENDIF MSWINDOWS} 248 | 249 | 250 | 251 | 252 | 253 | end. 254 | --------------------------------------------------------------------------------