Merge branch 'feature/module-config' into main (resolved conflicts)
Some checks failed
Build / idf-build (push) Has been cancelled

This commit is contained in:
2026-04-03 20:17:40 +08:00
17 changed files with 1053 additions and 31 deletions

View File

@@ -1,16 +1,131 @@
#include "tool_set_timezone.h"
#include "mimi_config.h"
#include "proxy/http_proxy.h"
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <sys/time.h>
#include "esp_log.h"
#include "esp_http_client.h"
#include "esp_crt_bundle.h"
#include "nvs.h"
#include "cJSON.h"
static const char *TAG = "tool_timezone";
static const char *MONTHS[] = {
"Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec"
};
typedef struct {
char date_val[64];
} tz_time_ctx_t;
static esp_err_t tz_http_event_handler(esp_http_client_event_t *evt)
{
tz_time_ctx_t *ctx = evt->user_data;
if (evt->event_id == HTTP_EVENT_ON_HEADER) {
if (strcasecmp(evt->header_key, "Date") == 0 && ctx) {
strncpy(ctx->date_val, evt->header_value, sizeof(ctx->date_val) - 1);
ctx->date_val[sizeof(ctx->date_val) - 1] = '\0';
}
}
return ESP_OK;
}
static bool parse_and_set_time_from_date(const char *date_str)
{
int day, year, hour, min, sec;
char mon_str[4] = {0};
if (sscanf(date_str, "%*[^,], %d %3s %d %d:%d:%d",
&day, mon_str, &year, &hour, &min, &sec) != 6) {
return false;
}
int mon = -1;
for (int i = 0; i < 12; i++) {
if (strcmp(mon_str, MONTHS[i]) == 0) { mon = i; break; }
}
if (mon < 0) return false;
struct tm tm = {
.tm_sec = sec, .tm_min = min, .tm_hour = hour,
.tm_mday = day, .tm_mon = mon, .tm_year = year - 1900,
};
setenv("TZ", "UTC0", 1);
tzset();
time_t t = mktime(&tm);
if (t < 0) return false;
struct timeval tv = { .tv_sec = t };
settimeofday(&tv, NULL);
return true;
}
static bool fetch_and_set_time(void)
{
if (http_proxy_is_enabled()) {
proxy_conn_t *conn = proxy_conn_open("api.telegram.org", 443, 10000);
if (!conn) return false;
const char *req = "HEAD / HTTP/1.1\r\nHost: api.telegram.org\r\nConnection: close\r\n\r\n";
if (proxy_conn_write(conn, req, strlen(req)) < 0) {
proxy_conn_close(conn);
return false;
}
char buf[1024];
int total = 0;
while (total < (int)sizeof(buf) - 1) {
int n = proxy_conn_read(conn, buf + total, sizeof(buf) - 1 - total, 10000);
if (n <= 0) break;
total += n;
buf[total] = '\0';
if (strstr(buf, "\r\n\r\n")) break;
}
proxy_conn_close(conn);
char *date_hdr = strcasestr(buf, "\r\nDate: ");
if (!date_hdr) return false;
date_hdr += 8;
char *eol = strstr(date_hdr, "\r\n");
if (!eol) return false;
char date_val[64];
size_t dlen = eol - date_hdr;
if (dlen >= sizeof(date_val)) return false;
memcpy(date_val, date_hdr, dlen);
date_val[dlen] = '\0';
return parse_and_set_time_from_date(date_val);
} else {
tz_time_ctx_t ctx = {0};
esp_http_client_config_t config = {
.url = "https://api.telegram.org/",
.method = HTTP_METHOD_HEAD,
.timeout_ms = 10000,
.crt_bundle_attach = esp_crt_bundle_attach,
.event_handler = tz_http_event_handler,
.user_data = &ctx,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
if (!client) return false;
esp_err_t err = esp_http_client_perform(client);
esp_http_client_cleanup(client);
if (err != ESP_OK || ctx.date_val[0] == '\0') return false;
return parse_and_set_time_from_date(ctx.date_val);
}
}
/* Common timezone mappings for user-friendly names */
typedef struct {
const char *name;
@@ -132,6 +247,18 @@ esp_err_t tool_set_timezone_execute(const char *input_json, char *output, size_t
tzset();
time_t now = time(NULL);
bool time_valid = (now > 1700000000); /* 2023-11-15 as sanity check */
if (!time_valid) {
ESP_LOGI(TAG, "System time appears invalid, fetching from NTP server...");
if (fetch_and_set_time()) {
now = time(NULL);
ESP_LOGI(TAG, "Time fetched via HTTP after timezone set");
} else {
ESP_LOGW(TAG, "Failed to fetch time via HTTP, waiting for SNTP sync");
}
}
struct tm tm_now;
localtime_r(&now, &tm_now);
char time_str[64];