├── 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 | 
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 |
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 |
--------------------------------------------------------------------------------