Update session.
This commit is contained in:
parent
a8d7015abd
commit
5cf55f423c
194
browse.h
194
browse.h
@ -1,201 +1,23 @@
|
|||||||
#include "http_curl.h"
|
#include "http_curl.h"
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
#include <json-c/json.h>
|
|
||||||
#include <json-c/json_util.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
char *url_encode(char *s) { return curl_easy_escape(NULL, s, 0); }
|
char *url_encode(char *s) { return curl_easy_escape(NULL, s, 0); }
|
||||||
|
|
||||||
char *web_search_news(char *q) {
|
|
||||||
const int MAX_RETRIES = 3;
|
|
||||||
for (int retry = 0; retry < MAX_RETRIES; retry++) {
|
|
||||||
char *news = malloc(4096);
|
|
||||||
if (!news)
|
|
||||||
return NULL;
|
|
||||||
news[0] = 0;
|
|
||||||
char *q_encoded = url_encode(q);
|
|
||||||
snprintf(
|
|
||||||
news, 4096,
|
|
||||||
"https://search.molodetz.nl/"
|
|
||||||
"search?q=%s&format=json&categories=news&preferences=eJx1WMuy4zYO_"
|
|
||||||
"Zp4o4ormaRqahZepZL5gexVFAlLiEhCzYdt3a8PoIdFWt2Ldl8fkCCIxwForRL0FBDirQc"
|
|
||||||
"PQdmLVb7PqoebyokulrSycPP2Il81uclCgtvlrh6oybcBItkHhNsFHW9qp0Cv-"
|
|
||||||
"faXshEuDtJA5vb_P_--RHWHCCro4fbLJQ3g4BZRdF1YQbYptqzLw7NNqtt2G8JDOyn-"
|
|
||||||
"eqXQX9ZtbUyz3UzU4BOEVlnsveO_t_3KPJTXYNrt3BX9liHMLfo2YWIFK4j-"
|
|
||||||
"jh4Ta9WBrN3QdZ8YplcvzazKgk63v0OGy0BphDneDNwV3-CSg23vFJxKCX1_"
|
|
||||||
"mwKkNF8MRtVZtgJ8j57d_L9e9W0bSaOyjQOD6qf__"
|
|
||||||
"MG366CJ7OCxbRdHxhptHmiA2nb571OmskGWuRxRi8hOfFJj0edXMyk9ijpWm0TmvYqN3As"
|
|
||||||
"f0LZ3tOtJ09g4DIFCibGfG_"
|
|
||||||
"6UU0K52CuOvFGbSo8vis0TR9yADi3Kv8LaDlOX9QhpX5IM9v2hcvHDVWt9NVBs0xN78Q4B"
|
|
||||||
"OI7bTg5QjAyyCzUKztgMU7M6rXCegJxQDMknAwbgi9Pk7SYDMamEvIA4IQMjEOiJ5vhu2G"
|
|
||||||
"T519NZfSFbzS3MLmS1BQf-BCWHHGet3nZq4uX8KdY4-"
|
|
||||||
"gcnccyx6tdXYcLdBBJzdydyDps0qOS4usplAThR6J6eKkBjMHAGSy6v_"
|
|
||||||
"rwH9CMqXW6Y58IJPeDXwPVQIFw5qtvjQQY6CP32lWXAgSS3fycyAZQptxP1fNXJqlmyKx4"
|
|
||||||
"XKCWOHgilU7muIEjNCO2UkRhUF5R8bEcOnPEQVsevALzeycNK4lWKZfmOznSHYej6HArF6"
|
|
||||||
"FVxPnr-EynH72N7VnHEAlPJXOj5B-NAxzFcHEGFuRG-"
|
|
||||||
"jVhc31IXE1zDbjfTp9KT8nudTbtT_"
|
|
||||||
"azUsY3MHKGsGprAb4ccpwrICoFqKMBEhQkrW2Dc65TLb2XBcwEcoirHp9xxDT-2_"
|
|
||||||
"QGMwXSiu0ApMVETx5SgSA0mS51jvE4zN4793loZM0tKubw5drEMfx7IwglfbZKsbOTjjTr"
|
|
||||||
"lE-omat6lQskeicaZEnGMRvH17ook9-TlKr_vlqp0EcaLE-"
|
|
||||||
"glTQ91JhvwxyouYoDx7L4Nr3y35PyEltKxP-Ru7sHtOTEBhJS7MtyLU3n1yJufT-"
|
|
||||||
"gK0az0fD46ZMcdqVhmuRdEDn2JPemFI3mOZRNnT34WTtmN4nwbQ237Cp3OWuETQ8ZHL5lX"
|
|
||||||
"LHwwlS0da7vonJ2zc1U1R2VEykHDkN9pOvbXnva8u4CvWy4PMC_"
|
|
||||||
"lTRAm3aYcYVzJ3bVJFs1LBU6TsgeHFz7K-Ko4llo6VqyVmw4GEGUbbRfLeCT4_"
|
|
||||||
"H721oJWbl2Qk_"
|
|
||||||
"u6qSuVSSHzkBRL1tcD3Efi8eXtQW0pmzsnPygsd3NHfRrJnRLk7lmt4eEG4QORtjyXGPsf"
|
|
||||||
"uczYhWWjNH3DI5LMV7gQd7GehKmbIe_"
|
|
||||||
"NRBSymdvgVawUnxqVKpce3bREefaCubIU0uyICdEXCXy3qMeS63k5ljTeCz3tbYKb2mHj2"
|
|
||||||
"qTKA7a2dYrmhlfx3LBTRDf8O_Q0bOcth49stoplzwDn5mYnwZWKaq5dV-"
|
|
||||||
"QI4Ucy7gDpRzLRzK44i79-freYty1bY6M7b_Nc4LH0hkghBeWj5SZc-o9D0z-"
|
|
||||||
"q0DrFI6ch_wOz3-JBxYF5-jsrzN49HL6WxD9C6-hrqArAPZkP6-"
|
|
||||||
"N5JIJ9npDqUjw1Sf5FSBXZ7zLuepxRasvwWh4TD2CJZ7p9sJuM8PexaOKXhHS7TYrymoFY"
|
|
||||||
"1tAkhFJky_L9ulzouNeEMt10qojIRMxNkUvCyAi0J_-7c5d3Ptr5npuFbLldY7hfo1_z_"
|
|
||||||
"HhCHEbmzomVxcXmaafVb08mhlLnAtS1sUKnMlrhU8UExZNX0zF5xXKqD9gPqTGqIoX1Zm6"
|
|
||||||
"OWGde5G6rdl8czYkyc3qdNPxU0CMxyd4tPfdWEcfcZZ_"
|
|
||||||
"y3pUyz345vhMnChNO8igujySLhufvsFbV96aNY3H2kYfhOJTPGjVQdbcFqB05U_6YEd7I-"
|
|
||||||
"_Gj0DItSsIWyx7oqkfmE7v5k4Y7ojF-"
|
|
||||||
"gtXxAnzLVLt6mZaWtn1C9xmqgsWyaoCVpdLkllGv1vCg-cPLggp1cTleTwlVCk9pVQo_"
|
|
||||||
"3tWlaInbEb4n2XtQjl_eQ92jUKcv8pVtjkcpxw-w5k2GphT_-ttv_30dmiN8eeWq_"
|
|
||||||
"cvkWCJePSTpypyoG_OTgvE4FgMcn62reuRul-QBKYo-yPSprB34CP_"
|
|
||||||
"hQ8c9vTolpXDFYvbldvCorr4A54Cs8HnKWeDP9lnpS7_vPyIcP7RMNvPYF2-"
|
|
||||||
"k2vUnpGfgsl86aUzsy7XeKLQ8IunxPSzu28QzPJW08nNO4EA91uFJWZ05VhSWsuQ3Dbc4F"
|
|
||||||
"qVtHrb3Fv2d5BSp11XZhZ8WzP-3fwFOiVFV",
|
|
||||||
q_encoded);
|
|
||||||
free(q_encoded);
|
|
||||||
|
|
||||||
char *ret = curl_get(news);
|
|
||||||
free(news);
|
|
||||||
if (!ret)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
json_object *json_ret = json_tokener_parse(ret);
|
|
||||||
if (!json_ret) {
|
|
||||||
free(ret);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
json_object *json_results = json_object_object_get(json_ret, "results");
|
|
||||||
json_object *json_result = json_object_array_get_idx(json_results, 0);
|
|
||||||
if (json_result) {
|
|
||||||
json_object_put(json_ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
json_object_put(json_ret);
|
|
||||||
free(ret);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *web_search(char *q) {
|
char *web_search(char *q) {
|
||||||
const int MAX_RETRIES = 3;
|
char url[4096];
|
||||||
for (int retry = 0; retry < MAX_RETRIES; retry++) {
|
|
||||||
char *news = malloc(4096);
|
|
||||||
if (!news)
|
|
||||||
return NULL;
|
|
||||||
news[0] = 0;
|
|
||||||
char *q_encoded = url_encode(q);
|
char *q_encoded = url_encode(q);
|
||||||
snprintf(
|
if (!q_encoded) {
|
||||||
news, 4096,
|
return NULL;
|
||||||
"https://search.molodetz.nl/"
|
}
|
||||||
"search?q=%s&format=json&preferences=eJx1WMuy4zYO_"
|
snprintf(url, sizeof(url), "https://static.molodetz.nl/search.cgi?query=%s", q_encoded);
|
||||||
"Zp4o4ormaRqahZepZL5gexVFAlLiEhCzYdt3a8PoIdFWt2Ldl8fkCCIxwForRL0FBDirQc"
|
|
||||||
"PQdmLVb7PqoebyokulrSycPP2Il81uclCgtvlrh6oybcBItkHhNsFHW9qp0Cv-"
|
|
||||||
"faXshEuDtJA5vb_P_--RHWHCCro4fbLJQ3g4BZRdF1YQbYptqzLw7NNqtt2G8JDOyn-"
|
|
||||||
"eqXQX9ZtbUyz3UzU4BOEVlnsveO_t_3KPJTXYNrt3BX9liHMLfo2YWIFK4j-"
|
|
||||||
"jh4Ta9WBrN3QdZ8YplcvzazKgk63v0OGy0BphDneDNwV3-CSg23vFJxKCX1_"
|
|
||||||
"mwKkNF8MRtVZtgJ8j57d_L9e9W0bSaOyjQOD6qf__"
|
|
||||||
"MG366CJ7OCxbRdHxhptHmiA2nb571OmskGWuRxRi8hOfFJj0edXMyk9ijpWm0TmvYqN3As"
|
|
||||||
"f0LZ3tOtJ09g4DIFCibGfG_"
|
|
||||||
"6UU0K52CuOvFGbSo8vis0TR9yADi3Kv8LaDlOX9QhpX5IM9v2hcvHDVWt9NVBs0xN78Q4B"
|
|
||||||
"OI7bTg5QjAyyCzUKztgMU7M6rXCegJxQDMknAwbgi9Pk7SYDMamEvIA4IQMjEOiJ5vhu2G"
|
|
||||||
"T519NZfSFbzS3MLmS1BQf-BCWHHGet3nZq4uX8KdY4-"
|
|
||||||
"gcnccyx6tdXYcLdBBJzdydyDps0qOS4usplAThR6J6eKkBjMHAGSy6v_"
|
|
||||||
"rwH9CMqXW6Y58IJPeDXwPVQIFw5qtvjQQY6CP32lWXAgSS3fycyAZQptxP1fNXJqlmyKx4"
|
|
||||||
"XKCWOHgilU7muIEjNCO2UkRhUF5R8bEcOnPEQVsevALzeycNK4lWKZfmOznSHYej6HArF6"
|
|
||||||
"FVxPnr-EynH72N7VnHEAlPJXOj5B-NAxzFcHEGFuRG-"
|
|
||||||
"jVhc31IXE1zDbjfTp9KT8nudTbtT_"
|
|
||||||
"azUsY3MHKGsGprAb4ccpwrICoFqKMBEhQkrW2Dc65TLb2XBcwEcoirHp9xxDT-2_"
|
|
||||||
"QGMwXSiu0ApMVETx5SgSA0mS51jvE4zN4793loZM0tKubw5drEMfx7IwglfbZKsbOTjjTr"
|
|
||||||
"lE-omat6lQskeicaZEnGMRvH17ook9-TlKr_vlqp0EcaLE-"
|
|
||||||
"glTQ91JhvwxyouYoDx7L4Nr3y35PyEltKxP-Ru7sHtOTEBhJS7MtyLU3n1yJufT-"
|
|
||||||
"gK0az0fD46ZMcdqVhmuRdEDn2JPemFI3mOZRNnT34WTtmN4nwbQ237Cp3OWuETQ8ZHL5lX"
|
|
||||||
"LHwwlS0da7vonJ2zc1U1R2VEykHDkN9pOvbXnva8u4CvWy4PMC_"
|
|
||||||
"lTRAm3aYcYVzJ3bVJFs1LBU6TsgeHFz7K-Ko4llo6VqyVmw4GEGUbbRfLeCT4_"
|
|
||||||
"H721oJWbl2Qk_"
|
|
||||||
"u6qSuVSSHzkBRL1tcD3Efi8eXtQW0pmzsnPygsd3NHfRrJnRLk7lmt4eEG4QORtjyXGPsf"
|
|
||||||
"uczYhWWjNH3DI5LMV7gQd7GehKmbIe_"
|
|
||||||
"NRBSymdvgVawUnxqVKpce3bREefaCubIU0uyICdEXCXy3qMeS63k5ljTeCz3tbYKb2mHj2"
|
|
||||||
"qTKA7a2dYrmhlfx3LBTRDf8O_Q0bOcth49stoplzwDn5mYnwZWKaq5dV-"
|
|
||||||
"QI4Ucy7gDpRzLRzK44i79-freYty1bY6M7b_Nc4LH0hkghBeWj5SZc-o9D0z-"
|
|
||||||
"q0DrFI6ch_wOz3-JBxYF5-jsrzN49HL6WxD9C6-hrqArAPZkP6-"
|
|
||||||
"N5JIJ9npDqUjw1Sf5FSBXZ7zLuepxRasvwWh4TD2CJZ7p9sJuM8PexaOKXhHS7TYrymoFY"
|
|
||||||
"1tAkhFJky_L9ulzouNeEMt10qojIRMxNkUvCyAi0J_-7c5d3Ptr5npuFbLldY7hfo1_z_"
|
|
||||||
"HhCHEbmzomVxcXmaafVb08mhlLnAtS1sUKnMlrhU8UExZNX0zF5xXKqD9gPqTGqIoX1Zm6"
|
|
||||||
"OWGde5G6rdl8czYkyc3qdNPxU0CMxyd4tPfdWEcfcZZ_"
|
|
||||||
"y3pUyz345vhMnChNO8igujySLhufvsFbV96aNY3H2kYfhOJTPGjVQdbcFqB05U_6YEd7I-"
|
|
||||||
"_Gj0DItSsIWyx7oqkfmE7v5k4Y7ojF-"
|
|
||||||
"gtXxAnzLVLt6mZaWtn1C9xmqgsWyaoCVpdLkllGv1vCg-cPLggp1cTleTwlVCk9pVQo_"
|
|
||||||
"3tWlaInbEb4n2XtQjl_eQ92jUKcv8pVtjkcpxw-w5k2GphT_-ttv_30dmiN8eeWq_"
|
|
||||||
"cvkWCJePSTpypyoG_OTgvE4FgMcn62reuRul-QBKYo-yPSprB34CP_"
|
|
||||||
"hQ8c9vTolpXDFYvbldvCorr4A54Cs8HnKWeDP9lnpS7_vPyIcP7RMNvPYF2-"
|
|
||||||
"k2vUnpGfgsl86aUzsy7XeKLQ8IunxPSzu28QzPJW08nNO4EA91uFJWZ05VhSWsuQ3Dbc4F"
|
|
||||||
"qVtHrb3Fv2d5BSp11XZhZ8WzP-3fwFOiVFV",
|
|
||||||
q_encoded);
|
|
||||||
free(q_encoded);
|
free(q_encoded);
|
||||||
|
char *ret = curl_get(url);
|
||||||
char *ret = curl_get(news);
|
|
||||||
free(news);
|
|
||||||
if (!ret)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
json_object *json_ret = json_tokener_parse(ret);
|
|
||||||
if (!json_ret) {
|
|
||||||
free(ret);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
json_object *json_results = json_object_object_get(json_ret, "results");
|
|
||||||
json_object *json_result = json_object_array_get_idx(json_results, 0);
|
|
||||||
if (json_result) {
|
|
||||||
json_object_put(json_ret);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
json_object_put(json_ret);
|
|
||||||
free(ret);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char *web_search_engine(char *q) {
|
char *web_search_news(char *q) {
|
||||||
const int MAX_RETRIES = 3;
|
return web_search(q);
|
||||||
for (int retry = 0; retry < MAX_RETRIES; retry++) {
|
|
||||||
char *searx = malloc(4096);
|
|
||||||
if (!searx)
|
|
||||||
return NULL;
|
|
||||||
searx[0] = 0;
|
|
||||||
snprintf(searx, 4096, "https://searx.molodetz.nl/search?q=%s&format=json",
|
|
||||||
q);
|
|
||||||
char *ret = curl_get(searx);
|
|
||||||
free(searx);
|
|
||||||
if (!ret)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
json_object *json_ret = json_tokener_parse(ret);
|
|
||||||
if (!json_ret) {
|
|
||||||
free(ret);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
json_object *json_results = json_object_object_get(json_ret, "results");
|
|
||||||
json_object *json_result = json_object_array_get_idx(json_results, 0);
|
|
||||||
if (json_result) {
|
|
||||||
json_object_put(json_ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
json_object_put(json_ret);
|
|
||||||
free(ret);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|||||||
89
http_curl.h
89
http_curl.h
@ -33,6 +33,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
struct ResponseBuffer {
|
struct ResponseBuffer {
|
||||||
char *data;
|
char *data;
|
||||||
@ -61,18 +62,36 @@ static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
|
|||||||
return total_size;
|
return total_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define HTTP_MAX_RETRIES 3
|
||||||
|
#define HTTP_RETRY_DELAY_MS 2000
|
||||||
|
|
||||||
char *curl_post(const char *url, const char *data) {
|
char *curl_post(const char *url, const char *data) {
|
||||||
CURL *curl;
|
CURL *curl;
|
||||||
CURLcode res;
|
CURLcode res;
|
||||||
struct ResponseBuffer response = {malloc(1), 0};
|
struct ResponseBuffer response = {NULL, 0};
|
||||||
|
int retry_count = 0;
|
||||||
|
|
||||||
if (!response.data)
|
while (retry_count < HTTP_MAX_RETRIES) {
|
||||||
|
if (response.data) {
|
||||||
|
free(response.data);
|
||||||
|
}
|
||||||
|
response.data = malloc(1);
|
||||||
|
response.size = 0;
|
||||||
|
if (!response.data) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
response.data[0] = '\0';
|
||||||
|
|
||||||
curl = curl_easy_init();
|
curl = curl_easy_init();
|
||||||
if (curl) {
|
if (!curl) {
|
||||||
|
free(response.data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct curl_slist *headers = NULL;
|
struct curl_slist *headers = NULL;
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 300L);
|
||||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||||
char bearer_header[1337];
|
char bearer_header[1337];
|
||||||
snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s",
|
snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s",
|
||||||
@ -82,16 +101,26 @@ char *curl_post(const char *url, const char *data) {
|
|||||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
|
||||||
|
|
||||||
res = curl_easy_perform(curl);
|
res = curl_easy_perform(curl);
|
||||||
if (res != CURLE_OK) {
|
|
||||||
fprintf(stderr, "Url: %s\n", data);
|
|
||||||
fprintf(stderr, "Data: %s\n", data);
|
|
||||||
fprintf(stderr, "An error occurred: %s\n", curl_easy_strerror(res));
|
|
||||||
}
|
|
||||||
curl_slist_free_all(headers);
|
curl_slist_free_all(headers);
|
||||||
curl_easy_cleanup(curl);
|
curl_easy_cleanup(curl);
|
||||||
|
|
||||||
|
if (res == CURLE_OK) {
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
retry_count++;
|
||||||
|
fprintf(stderr, "Network error: %s (attempt %d/%d)\n",
|
||||||
|
curl_easy_strerror(res), retry_count, HTTP_MAX_RETRIES);
|
||||||
|
|
||||||
|
if (retry_count < HTTP_MAX_RETRIES) {
|
||||||
|
fprintf(stderr, "Retrying in %d seconds...\n", HTTP_RETRY_DELAY_MS / 1000);
|
||||||
|
usleep(HTTP_RETRY_DELAY_MS * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Failed after %d attempts.\n", HTTP_MAX_RETRIES);
|
||||||
free(response.data);
|
free(response.data);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -99,15 +128,30 @@ char *curl_post(const char *url, const char *data) {
|
|||||||
char *curl_get(const char *url) {
|
char *curl_get(const char *url) {
|
||||||
CURL *curl;
|
CURL *curl;
|
||||||
CURLcode res;
|
CURLcode res;
|
||||||
struct ResponseBuffer response = {malloc(1), 0};
|
struct ResponseBuffer response = {NULL, 0};
|
||||||
|
int retry_count = 0;
|
||||||
|
|
||||||
if (!response.data)
|
while (retry_count < HTTP_MAX_RETRIES) {
|
||||||
|
if (response.data) {
|
||||||
|
free(response.data);
|
||||||
|
}
|
||||||
|
response.data = malloc(1);
|
||||||
|
response.size = 0;
|
||||||
|
if (!response.data) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
response.data[0] = '\0';
|
||||||
|
|
||||||
curl = curl_easy_init();
|
curl = curl_easy_init();
|
||||||
if (curl) {
|
if (!curl) {
|
||||||
|
free(response.data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct curl_slist *headers = NULL;
|
struct curl_slist *headers = NULL;
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L);
|
||||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||||
char bearer_header[1337];
|
char bearer_header[1337];
|
||||||
snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s",
|
snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s",
|
||||||
@ -116,17 +160,28 @@ char *curl_get(const char *url) {
|
|||||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
|
||||||
|
|
||||||
res = curl_easy_perform(curl);
|
res = curl_easy_perform(curl);
|
||||||
if (res != CURLE_OK) {
|
|
||||||
fprintf(stderr, "An error occurred: %s\n", curl_easy_strerror(res));
|
|
||||||
}
|
|
||||||
curl_slist_free_all(headers);
|
curl_slist_free_all(headers);
|
||||||
curl_easy_cleanup(curl);
|
curl_easy_cleanup(curl);
|
||||||
} else {
|
|
||||||
|
if (res == CURLE_OK) {
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
retry_count++;
|
||||||
|
fprintf(stderr, "Network error: %s (attempt %d/%d)\n",
|
||||||
|
curl_easy_strerror(res), retry_count, HTTP_MAX_RETRIES);
|
||||||
|
|
||||||
|
if (retry_count < HTTP_MAX_RETRIES) {
|
||||||
|
fprintf(stderr, "Retrying in %d seconds...\n", HTTP_RETRY_DELAY_MS / 1000);
|
||||||
|
usleep(HTTP_RETRY_DELAY_MS * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Failed after %d attempts.\n", HTTP_MAX_RETRIES);
|
||||||
free(response.data);
|
free(response.data);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
return response.data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
73
main.c
73
main.c
@ -114,6 +114,11 @@ static char *get_prompt_from_args(int argc, char **argv) {
|
|||||||
} else if (strcmp(argv[i], "--nh") == 0) {
|
} else if (strcmp(argv[i], "--nh") == 0) {
|
||||||
SYNTAX_HIGHLIGHT_ENABLED = false;
|
SYNTAX_HIGHLIGHT_ENABLED = false;
|
||||||
fprintf(stderr, "Syntax highlighting disabled.\n");
|
fprintf(stderr, "Syntax highlighting disabled.\n");
|
||||||
|
} else if (strncmp(argv[i], "--session=", 10) == 0) {
|
||||||
|
continue;
|
||||||
|
} else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--session") == 0) {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
} else {
|
} else {
|
||||||
strcat(system, argv[i]);
|
strcat(system, argv[i]);
|
||||||
strcat(system, (i < argc - 1) ? " " : ".");
|
strcat(system, (i < argc - 1) ? " " : ".");
|
||||||
@ -184,6 +189,17 @@ static void repl(void) {
|
|||||||
printf("%s\n", message_json());
|
printf("%s\n", message_json());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strncmp(line, "!clear", 6)) {
|
||||||
|
messages_remove();
|
||||||
|
fprintf(stderr, "Session cleared.\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(line, "!session", 8)) {
|
||||||
|
init_session_id();
|
||||||
|
printf("Session: %s\n", get_session_id());
|
||||||
|
printf("DB Key: %s\n", get_session_db_key());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!strncmp(line, "!verbose", 8)) {
|
if (!strncmp(line, "!verbose", 8)) {
|
||||||
is_verbose = !is_verbose;
|
is_verbose = !is_verbose;
|
||||||
fprintf(stderr, "%s\n",
|
fprintf(stderr, "%s\n",
|
||||||
@ -239,9 +255,15 @@ static void init(void) {
|
|||||||
char *schema = db_get_schema();
|
char *schema = db_get_schema();
|
||||||
char payload[1024 * 1024] = {0};
|
char payload[1024 * 1024] = {0};
|
||||||
|
|
||||||
|
time_t now = time(NULL);
|
||||||
|
struct tm *tm_info = localtime(&now);
|
||||||
|
char datetime[64];
|
||||||
|
strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S %Z", tm_info);
|
||||||
|
|
||||||
snprintf(
|
snprintf(
|
||||||
payload, sizeof(payload),
|
payload, sizeof(payload),
|
||||||
"# AUTONOMOUS AGENT INSTRUCTIONS\n"
|
"# AUTONOMOUS AGENT INSTRUCTIONS\n"
|
||||||
|
"Current date/time: %s\n\n"
|
||||||
"You are an autonomous AI agent. You operate in a loop: reason about the task, "
|
"You are an autonomous AI agent. You operate in a loop: reason about the task, "
|
||||||
"select and execute tools when needed, observe results, and continue until the goal is achieved.\n\n"
|
"select and execute tools when needed, observe results, and continue until the goal is achieved.\n\n"
|
||||||
"## Reasoning Pattern (ReAct)\n"
|
"## Reasoning Pattern (ReAct)\n"
|
||||||
@ -253,16 +275,24 @@ static void init(void) {
|
|||||||
"## Tool Usage\n"
|
"## Tool Usage\n"
|
||||||
"- Use tools proactively to gather information and take actions\n"
|
"- Use tools proactively to gather information and take actions\n"
|
||||||
"- If a tool fails, analyze the error and try a different approach\n"
|
"- If a tool fails, analyze the error and try a different approach\n"
|
||||||
"- You can call multiple tools in sequence to accomplish complex tasks\n"
|
"- You can call multiple tools in sequence to accomplish complex tasks\n\n"
|
||||||
"- Always verify results before concluding\n\n"
|
"## CRITICAL OUTPUT RULES\n"
|
||||||
|
"- You MUST include the actual content/data from tool results in your response\n"
|
||||||
|
"- When you search the web, QUOTE the relevant information found\n"
|
||||||
|
"- When you run a command, SHOW the output\n"
|
||||||
|
"- NEVER say 'I found information' without showing what you found\n"
|
||||||
|
"- NEVER say 'task complete' or 'report provided' - SHOW THE ACTUAL DATA\n"
|
||||||
|
"- The user cannot see tool results - only YOUR response. Include everything relevant.\n\n"
|
||||||
"## Local Database\n"
|
"## Local Database\n"
|
||||||
"You have a local SQLite database accessible via db_query, db_get, and db_set tools.\n"
|
"You have a local SQLite database accessible via db_query, db_get, and db_set tools.\n"
|
||||||
"Use stemmed, lowercase keys to prevent duplicates.\n"
|
"Use stemmed, lowercase keys to prevent duplicates.\n"
|
||||||
"Schema: %s\n\n"
|
"Schema: %s\n\n"
|
||||||
"## Completion\n"
|
"## Response Format\n"
|
||||||
"When the task is fully complete, provide a clear final response.\n"
|
"Your response IS the only thing the user sees. Tool outputs are hidden from them.\n"
|
||||||
"Do not stop mid-task. Continue using tools until the goal is achieved.\n",
|
"You MUST copy/paste relevant data from tool results into your response.\n"
|
||||||
schema);
|
"Bad: 'I searched and found information about X.'\n"
|
||||||
|
"Good: 'Here is what I found: [actual content from search results]'\n",
|
||||||
|
datetime, schema);
|
||||||
|
|
||||||
free(schema);
|
free(schema);
|
||||||
fprintf(stderr, "Loading...");
|
fprintf(stderr, "Loading...");
|
||||||
@ -297,15 +327,46 @@ static void handle_sigint(int sig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_session_arg(int argc, char *argv[]) {
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (strncmp(argv[i], "--session=", 10) == 0) {
|
||||||
|
const char *name = argv[i] + 10;
|
||||||
|
if (!set_session_id(name)) {
|
||||||
|
fprintf(stderr, "Error: Invalid session name '%s'\n", name);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--session") == 0) && i + 1 < argc) {
|
||||||
|
const char *name = argv[++i];
|
||||||
|
if (!set_session_id(name)) {
|
||||||
|
fprintf(stderr, "Error: Invalid session name '%s'\n", name);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char *env_session = get_env_session();
|
||||||
|
if (env_session) {
|
||||||
|
if (!set_session_id(env_session)) {
|
||||||
|
fprintf(stderr, "Warning: Invalid R_SESSION '%s', using default\n", env_session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
signal(SIGINT, handle_sigint);
|
signal(SIGINT, handle_sigint);
|
||||||
|
|
||||||
|
parse_session_arg(argc, argv);
|
||||||
init();
|
init();
|
||||||
char *env_string = get_env_string();
|
char *env_string = get_env_string();
|
||||||
if (env_string && *env_string) {
|
if (env_string && *env_string) {
|
||||||
openai_system(env_string);
|
openai_system(env_string);
|
||||||
free(env_string);
|
free(env_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
messages_load_conversation();
|
||||||
|
|
||||||
if (try_prompt(argc, argv))
|
if (try_prompt(argc, argv))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|||||||
136
messages.h
136
messages.h
@ -28,8 +28,129 @@
|
|||||||
#include "db_utils.h"
|
#include "db_utils.h"
|
||||||
#include "json-c/json.h"
|
#include "json-c/json.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
#include "utils.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
struct json_object *message_array = NULL;
|
struct json_object *message_array = NULL;
|
||||||
|
static char session_id[256] = {0};
|
||||||
|
static char override_session_id[256] = {0};
|
||||||
|
|
||||||
|
struct json_object *message_list();
|
||||||
|
|
||||||
|
bool set_session_id(const char *name) {
|
||||||
|
if (name == NULL || *name == '\0') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t len = strlen(name);
|
||||||
|
if (len > 200) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const char *p = name; *p; p++) {
|
||||||
|
if (*p == '/' || *p == '\\') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strncpy(override_session_id, name, sizeof(override_session_id) - 1);
|
||||||
|
override_session_id[sizeof(override_session_id) - 1] = '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *get_session_id() {
|
||||||
|
if (session_id[0] == '\0') {
|
||||||
|
if (override_session_id[0] != '\0') {
|
||||||
|
strncpy(session_id, override_session_id, sizeof(session_id) - 1);
|
||||||
|
session_id[sizeof(session_id) - 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return session_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_session_id() {
|
||||||
|
if (session_id[0] != '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (override_session_id[0] != '\0') {
|
||||||
|
strncpy(session_id, override_session_id, sizeof(session_id) - 1);
|
||||||
|
session_id[sizeof(session_id) - 1] = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char *tty = ttyname(STDIN_FILENO);
|
||||||
|
if (tty) {
|
||||||
|
unsigned long h = 5381;
|
||||||
|
for (char *p = tty; *p; p++) {
|
||||||
|
h = ((h << 5) + h) + (unsigned char)*p;
|
||||||
|
}
|
||||||
|
snprintf(session_id, sizeof(session_id), "%lu", h);
|
||||||
|
} else {
|
||||||
|
snprintf(session_id, sizeof(session_id), "%d", getppid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *get_session_db_key() {
|
||||||
|
static char key[512];
|
||||||
|
init_session_id();
|
||||||
|
snprintf(key, sizeof(key), "session:%s", session_id);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool messages_save() {
|
||||||
|
char *key = get_session_db_key();
|
||||||
|
const char *json_str = json_object_to_json_string_ext(message_list(), JSON_C_TO_STRING_PLAIN);
|
||||||
|
if (!json_str) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
json_object *result = db_set(key, json_str);
|
||||||
|
if (result) {
|
||||||
|
json_object_put(result);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool messages_load_conversation() {
|
||||||
|
char *key = get_session_db_key();
|
||||||
|
json_object *result = db_get(key);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
json_object *value_obj;
|
||||||
|
if (!json_object_object_get_ex(result, "value", &value_obj)) {
|
||||||
|
json_object_put(result);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const char *json_str = json_object_get_string(value_obj);
|
||||||
|
if (!json_str) {
|
||||||
|
json_object_put(result);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
struct json_object *loaded = json_tokener_parse(json_str);
|
||||||
|
json_object_put(result);
|
||||||
|
if (!loaded || !json_object_is_type(loaded, json_type_array)) {
|
||||||
|
if (loaded) {
|
||||||
|
json_object_put(loaded);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int len = json_object_array_length(loaded);
|
||||||
|
int added = 0;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
struct json_object *msg = json_object_array_get_idx(loaded, i);
|
||||||
|
struct json_object *role_obj;
|
||||||
|
if (json_object_object_get_ex(msg, "role", &role_obj)) {
|
||||||
|
const char *role = json_object_get_string(role_obj);
|
||||||
|
if (role && strcmp(role, "system") != 0) {
|
||||||
|
json_object_array_add(message_list(), json_object_get(msg));
|
||||||
|
added++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
json_object_array_add(message_list(), json_object_get(msg));
|
||||||
|
added++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json_object_put(loaded);
|
||||||
|
return added > 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct json_object *message_list() {
|
struct json_object *message_list() {
|
||||||
if (!message_array) {
|
if (!message_array) {
|
||||||
@ -42,19 +163,25 @@ bool messages_remove_last() {
|
|||||||
int size = json_object_array_length(messages);
|
int size = json_object_array_length(messages);
|
||||||
if (size) {
|
if (size) {
|
||||||
json_object_array_del_idx(messages, size - 1, 1);
|
json_object_array_del_idx(messages, size - 1, 1);
|
||||||
|
messages_save();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void messages_remove() {
|
void messages_remove() {
|
||||||
while (messages_remove_last())
|
if (message_array) {
|
||||||
continue;
|
json_object_put(message_array);
|
||||||
|
message_array = NULL;
|
||||||
|
}
|
||||||
|
message_list();
|
||||||
|
messages_save();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct json_object *message_add_tool_call(struct json_object *message) {
|
struct json_object *message_add_tool_call(struct json_object *message) {
|
||||||
struct json_object *messages = message_list();
|
struct json_object *messages = message_list();
|
||||||
json_object_array_add(messages, message);
|
json_object_array_add(messages, message);
|
||||||
|
messages_save();
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,12 +204,14 @@ struct json_object *message_add_tool_result(const char *tool_call_id,
|
|||||||
json_object_new_string_len(tool_result, (int)result_len));
|
json_object_new_string_len(tool_result, (int)result_len));
|
||||||
|
|
||||||
json_object_array_add(messages, message);
|
json_object_array_add(messages, message);
|
||||||
|
messages_save();
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
void message_add_object(json_object *message) {
|
void message_add_object(json_object *message) {
|
||||||
struct json_object *messages = message_list();
|
struct json_object *messages = message_list();
|
||||||
json_object_array_add(messages, message);
|
json_object_array_add(messages, message);
|
||||||
|
messages_save();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct json_object *message_add(const char *role, const char *content);
|
struct json_object *message_add(const char *role, const char *content);
|
||||||
@ -110,6 +239,9 @@ struct json_object *message_add(const char *role, const char *content) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
json_object_array_add(messages, message);
|
json_object_array_add(messages, message);
|
||||||
|
if (strcmp(role, "system") != 0) {
|
||||||
|
messages_save();
|
||||||
|
}
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
5
r.h
5
r.h
@ -58,6 +58,11 @@ char *get_env_system_message() {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *get_env_session() {
|
||||||
|
char *session = getenv("R_SESSION");
|
||||||
|
return (session && *session) ? session : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
bool get_use_strict() {
|
bool get_use_strict() {
|
||||||
if (getenv("R_USE_STRICT") != NULL) {
|
if (getenv("R_USE_STRICT") != NULL) {
|
||||||
const char *value = getenv("R_USE_STRICT");
|
const char *value = getenv("R_USE_STRICT");
|
||||||
|
|||||||
4
tools.h
4
tools.h
@ -232,9 +232,9 @@ char *tool_function_web_search(char *query) {
|
|||||||
return strdup("Query cannot be NULL.");
|
return strdup("Query cannot be NULL.");
|
||||||
}
|
}
|
||||||
|
|
||||||
char *result = web_search_news(query);
|
char *result = web_search(query);
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
return strdup("Failed to fetch news.");
|
return strdup("Failed to search.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user