calendar

package
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 1, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Index

Constants

View Source
const (
	PrimaryCalendarIDStr = "primary"
)

Variables

View Source
var CalendarAgenda = common.Shortcut{
	Service:     "calendar",
	Command:     "+agenda",
	Description: "View calendar agenda (defaults to today)",
	Risk:        "read",
	Scopes:      []string{"calendar:calendar.event:read"},
	AuthTypes:   []string{"user", "bot"},
	HasFormat:   true,
	Flags: []common.Flag{
		{Name: "start", Desc: "start time (ISO 8601, default: start of today)"},
		{Name: "end", Desc: "end time (ISO 8601, default: end of start day)"},
		{Name: "calendar-id", Desc: "calendar ID (default: primary)"},
	},
	DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
		startInt, endInt, err := parseTimeRange(runtime)
		if err != nil {
			return common.NewDryRunAPI().Set("error", err.Error())
		}
		calendarId := runtime.Str("calendar-id")
		d := common.NewDryRunAPI()
		switch calendarId {
		case "":
			d.Desc("(calendar-id omitted) Will use primary calendar")
			calendarId = "<primary>"
		case "primary":
			calendarId = "<primary>"
		}
		return d.
			GET("/open-apis/calendar/v4/calendars/:calendar_id/events/instance_view").
			Params(map[string]interface{}{"start_time": fmt.Sprintf("%d", startInt), "end_time": fmt.Sprintf("%d", endInt)}).
			Set("calendar_id", calendarId)
	},
	Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
		startInt, endInt, err := parseTimeRange(runtime)
		if err != nil {
			return err
		}
		calendarId := strings.TrimSpace(runtime.Str("calendar-id"))
		if calendarId == "" {
			calendarId = PrimaryCalendarIDStr
		}

		items, err := fetchInstanceViewRange(ctx, runtime, calendarId, startInt, endInt, 0)
		if err != nil {
			return err
		}
		visible := dedupeAndSortItems(items)

		filtered := make([]map[string]interface{}, 0)
		for _, e := range visible {
			status, _ := e["status"].(string)
			if status != "cancelled" {
				delete(e, "status")
				delete(e, "attendees")

				if startMap, ok := e["start_time"].(map[string]interface{}); ok {
					if tsStr, ok := startMap["timestamp"].(string); ok && tsStr != "" {
						if ts, err := strconv.ParseInt(tsStr, 10, 64); err == nil {
							startMap["datetime"] = time.Unix(ts, 0).Local().Format(time.RFC3339)
							delete(startMap, "timestamp")
						}
					}
				}
				if endMap, ok := e["end_time"].(map[string]interface{}); ok {
					if tsStr, ok := endMap["timestamp"].(string); ok && tsStr != "" {
						if ts, err := strconv.ParseInt(tsStr, 10, 64); err == nil {
							endMap["datetime"] = time.Unix(ts, 0).Local().Format(time.RFC3339)
							delete(endMap, "timestamp")
						}
					}

					if dt, _ := endMap["datetime"].(string); dt == "" {
						if dateStr, ok := endMap["date"].(string); ok && dateStr != "" {
							if t, err := time.ParseInLocation("2006-01-02", dateStr, time.UTC); err == nil {
								endMap["date"] = t.Add(-1 * time.Second).Format("2006-01-02")
							}
						}
					}
				}

				filtered = append(filtered, e)
			}
		}

		runtime.OutFormat(filtered, &output.Meta{Count: len(filtered)}, func(w io.Writer) {
			if len(filtered) == 0 {
				fmt.Fprintln(w, "No events in this time range.")
				return
			}

			var rows []map[string]interface{}
			for _, e := range filtered {
				summary, _ := e["summary"].(string)
				if summary == "" {
					summary = "(untitled)"
				}
				summary = common.TruncateStr(summary, 40)
				startMap, _ := e["start_time"].(map[string]interface{})
				endMap, _ := e["end_time"].(map[string]interface{})
				startStr, _ := startMap["datetime"].(string)
				if startStr == "" {
					startStr, _ = startMap["date"].(string)
				}
				endStr, _ := endMap["datetime"].(string)
				if endStr == "" {
					endStr, _ = endMap["date"].(string)
				}
				freeBusyStatus, _ := e["free_busy_status"].(string)
				selfRsvpStatus, _ := e["self_rsvp_status"].(string)
				eventId, _ := e["event_id"].(string)
				rows = append(rows, map[string]interface{}{
					"event_id":         eventId,
					"summary":          summary,
					"start":            startStr,
					"end":              endStr,
					"free_busy_status": freeBusyStatus,
					"self_rsvp_status": selfRsvpStatus,
				})
			}
			output.PrintTable(w, rows)
			fmt.Fprintf(w, "\n%d event(s) total\n", len(filtered))
		})
		return nil
	},
}
View Source
var CalendarCreate = common.Shortcut{
	Service:     "calendar",
	Command:     "+create",
	Description: "Create a calendar event and optionally invite attendees",
	Risk:        "write",
	Scopes:      []string{"calendar:calendar.event:create", "calendar:calendar.event:update"},
	AuthTypes:   []string{"user", "bot"},
	Flags: []common.Flag{
		{Name: "summary", Desc: "event title"},
		{Name: "start", Desc: "start time (ISO 8601)", Required: true},
		{Name: "end", Desc: "end time (ISO 8601)", Required: true},
		{Name: "description", Desc: "event description"},
		{Name: "attendee-ids", Desc: "attendee IDs, comma-separated (supports user ou_, chat oc_, room omm_)"},
		{Name: "calendar-id", Desc: "calendar ID (default: primary)"},
		{Name: "rrule", Desc: "recurrence rule (rfc5545)"},
	},
	Validate: func(ctx context.Context, runtime *common.RuntimeContext) error {
		for _, flag := range []string{"summary", "description", "rrule", "calendar-id"} {
			if val := runtime.Str(flag); val != "" {
				if err := common.RejectDangerousChars("--"+flag, val); err != nil {
					return output.ErrValidation(err.Error())
				}
			}
		}

		if attendeesStr := runtime.Str("attendee-ids"); attendeesStr != "" {
			for _, id := range strings.Split(attendeesStr, ",") {
				id = strings.TrimSpace(id)
				if id == "" {
					continue
				}
				if !strings.HasPrefix(id, "ou_") && !strings.HasPrefix(id, "oc_") && !strings.HasPrefix(id, "omm_") {
					return output.ErrValidation("invalid attendee id format %q: should start with 'ou_', 'oc_', or 'omm_'", id)
				}
			}
		}

		if runtime.Str("start") == "" {
			return common.FlagErrorf("specify --start (e.g. '2026-03-12T14:00+08:00')")
		}
		if runtime.Str("end") == "" {
			return common.FlagErrorf("specify --end (e.g. '2026-03-12T15:00+08:00')")
		}
		startTs, err := common.ParseTime(runtime.Str("start"))
		if err != nil {
			return common.FlagErrorf("--start: %v", err)
		}
		endTs, err := common.ParseTime(runtime.Str("end"), "end")
		if err != nil {
			return common.FlagErrorf("--end: %v", err)
		}
		s, err := strconv.ParseInt(startTs, 10, 64)
		if err != nil {
			return common.FlagErrorf("invalid start time: %v", err)
		}
		e, err := strconv.ParseInt(endTs, 10, 64)
		if err != nil {
			return common.FlagErrorf("invalid end time: %v", err)
		}
		if e <= s {
			return common.FlagErrorf("end time must be after start time")
		}
		return nil
	},
	DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
		calendarId := runtime.Str("calendar-id")
		d := common.NewDryRunAPI()
		switch calendarId {
		case "":
			d.Desc("(calendar-id omitted) Will use primary calendar")
			calendarId = "<primary>"
		case "primary":
			calendarId = "<primary>"
		}
		startTs, err := common.ParseTime(runtime.Str("start"))
		if err != nil {
			return common.NewDryRunAPI().Set("error", fmt.Sprintf("--start: %v", err))
		}
		endTs, err := common.ParseTime(runtime.Str("end"), "end")
		if err != nil {
			return common.NewDryRunAPI().Set("error", fmt.Sprintf("--end: %v", err))
		}
		eventData := buildEventData(runtime, startTs, endTs)
		attendeesStr := runtime.Str("attendee-ids")
		if attendeesStr != "" {

			attendees, err := parseAttendees(attendeesStr, "")
			if err != nil {
				return common.NewDryRunAPI().Set("error", err.Error())
			}

			d.Desc("2-step: create event → add attendees (auto-rollback on failure)").
				POST("/open-apis/calendar/v4/calendars/:calendar_id/events").
				Desc("[1/2] Create event").
				Body(eventData).
				POST("/open-apis/calendar/v4/calendars/:calendar_id/events/<event_id>/attendees").
				Desc("[2/2] Add attendees (on failure: auto-delete event)").
				Params(map[string]interface{}{"user_id_type": "open_id"}).
				Body(map[string]interface{}{"attendees": attendees, "need_notification": true})
		} else {
			d.POST("/open-apis/calendar/v4/calendars/:calendar_id/events").
				Body(eventData)
		}
		return d.Set("calendar_id", calendarId)
	},
	Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
		calendarId := strings.TrimSpace(runtime.Str("calendar-id"))
		if calendarId == "" {
			calendarId = PrimaryCalendarIDStr
		}

		startTs, err := common.ParseTime(runtime.Str("start"))
		if err != nil {
			return output.ErrValidation("--start: %v", err)
		}
		endTs, err := common.ParseTime(runtime.Str("end"), "end")
		if err != nil {
			return output.ErrValidation("--end: %v", err)
		}

		eventData := buildEventData(runtime, startTs, endTs)

		data, err := runtime.CallAPI("POST",
			fmt.Sprintf("/open-apis/calendar/v4/calendars/%s/events", validate.EncodePathSegment(calendarId)),
			nil, eventData)
		if err != nil {
			return err
		}
		event, _ := data["event"].(map[string]interface{})
		eventId, _ := event["event_id"].(string)
		if eventId == "" {
			return output.Errorf(output.ExitAPI, "api_error", "failed to create event: no event_id returned")
		}

		if attendeesStr := runtime.Str("attendee-ids"); attendeesStr != "" {
			currentUserId := ""
			if !runtime.IsBot() {
				currentUserId = runtime.UserOpenId()
			}
			attendees, err := parseAttendees(attendeesStr, currentUserId)
			if err != nil {
				return output.ErrValidation("invalid attendee id: %v", err)
			}

			_, err = runtime.CallAPI("POST",
				fmt.Sprintf("/open-apis/calendar/v4/calendars/%s/events/%s/attendees", validate.EncodePathSegment(calendarId), validate.EncodePathSegment(eventId)),
				map[string]interface{}{"user_id_type": "open_id"},
				map[string]interface{}{
					"attendees":         attendees,
					"need_notification": true,
				})
			if err != nil {

				_, rollbackErr := runtime.RawAPI("DELETE",
					fmt.Sprintf("/open-apis/calendar/v4/calendars/%s/events/%s", validate.EncodePathSegment(calendarId), validate.EncodePathSegment(eventId)),
					map[string]interface{}{"need_notification": false}, nil)
				if rollbackErr != nil {
					return output.Errorf(output.ExitAPI, "api_error", "failed to add attendees: %v; rollback also failed, orphan event_id=%s needs manual cleanup", rollbackErr, eventId)
				}
				return output.Errorf(output.ExitAPI, "api_error", "failed to add attendees: %v; event rolled back successfully", err)
			}
		}

		startMap, _ := event["start_time"].(map[string]interface{})
		endMap, _ := event["end_time"].(map[string]interface{})

		if startMap != nil {
			if tsStr, ok := startMap["timestamp"].(string); ok && tsStr != "" {
				if ts, err := strconv.ParseInt(tsStr, 10, 64); err == nil {
					startMap["datetime"] = time.Unix(ts, 0).Local().Format(time.RFC3339)
					delete(startMap, "timestamp")
				}
			}
		}
		if endMap != nil {
			if tsStr, ok := endMap["timestamp"].(string); ok && tsStr != "" {
				if ts, err := strconv.ParseInt(tsStr, 10, 64); err == nil {
					endMap["datetime"] = time.Unix(ts, 0).Local().Format(time.RFC3339)
					delete(endMap, "timestamp")
				}
			}

			if dt, _ := endMap["datetime"].(string); dt == "" {
				if dateStr, ok := endMap["date"].(string); ok && dateStr != "" {
					if t, err := time.ParseInLocation("2006-01-02", dateStr, time.UTC); err == nil {
						endMap["date"] = t.Add(-1 * time.Second).Format("2006-01-02")
					}
				}
			}
		}

		var startStr, endStr string
		if startMap != nil {
			startStr, _ = startMap["datetime"].(string)
			if startStr == "" {
				startStr, _ = startMap["date"].(string)
			}
		}
		if endMap != nil {
			endStr, _ = endMap["datetime"].(string)
			if endStr == "" {
				endStr, _ = endMap["date"].(string)
			}
		}

		runtime.Out(map[string]interface{}{
			"event_id": eventId,
			"summary":  event["summary"],
			"start":    startStr,
			"end":      endStr,
		}, nil)
		return nil
	},
}
View Source
var CalendarFreebusy = common.Shortcut{
	Service:     "calendar",
	Command:     "+freebusy",
	Description: "Query user free/busy and RSVP status",
	Risk:        "read",
	Scopes:      []string{"calendar:calendar.free_busy:read"},
	AuthTypes:   []string{"user", "bot"},
	HasFormat:   true,
	Flags: []common.Flag{
		{Name: "start", Desc: "start time (ISO 8601, default: today)"},
		{Name: "end", Desc: "end time (ISO 8601, default: end of start day)"},
		{Name: "user-id", Desc: "target user open_id (ou_ prefix, default: current user)"},
	},
	DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
		userId := runtime.Str("user-id")
		if userId == "" {
			userId = runtime.UserOpenId()
		}
		timeMin, timeMax, err := parseFreebusyTimeRange(runtime)
		if err != nil {
			return common.NewDryRunAPI().Set("error", err.Error())
		}
		return common.NewDryRunAPI().
			POST("/open-apis/calendar/v4/freebusy/list").
			Body(map[string]interface{}{"time_min": timeMin, "time_max": timeMax, "user_id": userId, "need_rsvp_status": true})
	},
	Validate: func(ctx context.Context, runtime *common.RuntimeContext) error {
		userId := runtime.Str("user-id")
		if userId == "" && runtime.IsBot() {
			return common.FlagErrorf("--user-id is required for bot identity")
		}
		if userId == "" && runtime.UserOpenId() == "" {
			return common.FlagErrorf("cannot determine user ID, specify --user-id or ensure you are logged in")
		}
		if userId != "" {
			if _, err := common.ValidateUserID(userId); err != nil {
				return err
			}
		}
		return nil
	},
	Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
		userId := runtime.Str("user-id")
		if userId == "" {
			userId = runtime.UserOpenId()
		}

		timeMin, timeMax, err := parseFreebusyTimeRange(runtime)
		if err != nil {
			return output.ErrValidation("--start/--end: %v", err)
		}

		data, err := runtime.CallAPI("POST", "/open-apis/calendar/v4/freebusy/list", nil, map[string]interface{}{
			"time_min":         timeMin,
			"time_max":         timeMax,
			"user_id":          userId,
			"need_rsvp_status": true,
		})
		if err != nil {
			return err
		}
		items, _ := data["freebusy_list"].([]interface{})

		runtime.OutFormat(items, &output.Meta{Count: len(items)}, func(w io.Writer) {
			if len(items) == 0 {
				fmt.Fprintln(w, "No busy periods in this time range.")
				return
			}

			var rows []map[string]interface{}
			for _, item := range items {
				m, ok := item.(map[string]interface{})
				if !ok {
					continue
				}
				rows = append(rows, map[string]interface{}{
					"start": m["start_time"],
					"end":   m["end_time"],
				})
			}
			output.PrintTable(w, rows)
			fmt.Fprintf(w, "\n%d busy period(s) total\n", len(items))
		})
		return nil
	},
}
View Source
var CalendarRsvp = common.Shortcut{
	Service:     "calendar",
	Command:     "+rsvp",
	Description: "Reply to a calendar event (accept/decline/tentative)",
	Risk:        "write",
	Scopes:      []string{"calendar:calendar.event:reply"},
	AuthTypes:   []string{"user", "bot"},
	HasFormat:   false,
	Flags: []common.Flag{
		{Name: "calendar-id", Desc: "calendar ID (default: primary)"},
		{Name: "event-id", Desc: "event ID", Required: true},
		{Name: "rsvp-status", Desc: "reply status", Required: true, Enum: []string{"accept", "decline", "tentative"}},
	},
	DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
		calendarId := strings.TrimSpace(runtime.Str("calendar-id"))
		d := common.NewDryRunAPI()
		switch calendarId {
		case "":
			d.Desc("(calendar-id omitted) Will use primary calendar")
			calendarId = "<primary>"
		case "primary":
			calendarId = "<primary>"
		}
		eventId := strings.TrimSpace(runtime.Str("event-id"))
		status := strings.TrimSpace(runtime.Str("rsvp-status"))

		return d.
			POST("/open-apis/calendar/v4/calendars/:calendar_id/events/:event_id/reply").
			Body(map[string]interface{}{"rsvp_status": status}).
			Set("calendar_id", calendarId).
			Set("event_id", eventId)
	},
	Validate: func(ctx context.Context, runtime *common.RuntimeContext) error {
		for _, flag := range []string{"calendar-id", "event-id", "rsvp-status"} {
			if val := strings.TrimSpace(runtime.Str(flag)); val != "" {
				if err := common.RejectDangerousChars("--"+flag, val); err != nil {
					return output.ErrValidation(err.Error())
				}
			}
		}

		eventId := strings.TrimSpace(runtime.Str("event-id"))
		if eventId == "" {
			return output.ErrValidation("event-id cannot be empty")
		}
		return nil
	},
	Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
		calendarId := strings.TrimSpace(runtime.Str("calendar-id"))
		if calendarId == "" {
			calendarId = PrimaryCalendarIDStr
		}
		eventId := strings.TrimSpace(runtime.Str("event-id"))
		status := strings.TrimSpace(runtime.Str("rsvp-status"))

		_, err := runtime.DoAPIJSON("POST",
			fmt.Sprintf("/open-apis/calendar/v4/calendars/%s/events/%s/reply",
				validate.EncodePathSegment(calendarId),
				validate.EncodePathSegment(eventId)),
			nil,
			map[string]interface{}{
				"rsvp_status": status,
			})
		if err != nil {
			return err
		}

		runtime.Out(map[string]interface{}{
			"calendar_id": calendarId,
			"event_id":    eventId,
			"rsvp_status": status,
		}, nil)
		return nil
	},
}
View Source
var CalendarSuggestion = common.Shortcut{
	Service:     "calendar",
	Command:     "+suggestion",
	Description: "Intelligently suggest available meeting times to simplify scheduling",
	Risk:        "read",
	Scopes:      []string{"calendar:calendar.free_busy:read"},
	AuthTypes:   []string{"user", "bot"},
	HasFormat:   true,
	Flags: []common.Flag{
		{Name: flagStart, Type: "string", Desc: "search start time (ISO 8601, default: current time)"},
		{Name: flagEnd, Type: "string", Desc: "search end time (ISO 8601, default: end of start day)"},
		{Name: flagAttendees, Type: "string", Desc: "attendee IDs, comma-separated (supports user (open_id) ou_xxx, or chat oc_xxx) ids"},
		{Name: flagEventRrule, Type: "string", Desc: "event recurrence rules"},
		{Name: flagDurationMinutes, Type: "int", Desc: "duration (minutes)"},
		{Name: flagTimezone, Type: "string", Desc: "current time zone"},
		{Name: flagExclude, Type: "string", Desc: "excluded event times (ISO 8601, e.g. '2026-03-19T10:00:00+08:00~2026-03-19T11:00:00+08:00'), comma-separated"},
	},
	DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
		req, err := buildSuggestionRequest(runtime)
		if err != nil {
			return common.NewDryRunAPI().Set("error", err.Error())
		}
		return common.NewDryRunAPI().
			POST(suggestionPath).
			Body(req)
	},
	Validate: func(ctx context.Context, runtime *common.RuntimeContext) error {
		durationMinutes := runtime.Int(flagDurationMinutes)
		if durationMinutes != 0 && (durationMinutes < 1 || durationMinutes > 1440) {
			return output.ErrValidation("--duration-minutes must be between 1 and 1440")
		}

		for _, flag := range []string{flagEventRrule, flagTimezone} {
			if val := runtime.Str(flag); val != "" {
				if err := common.RejectDangerousChars("--"+flag, val); err != nil {
					return output.ErrValidation(err.Error())
				}
			}
		}

		if attendeesStr := runtime.Str(flagAttendees); attendeesStr != "" {
			for _, id := range strings.Split(attendeesStr, ",") {
				id = strings.TrimSpace(id)
				if id == "" {
					continue
				}
				if !strings.HasPrefix(id, "ou_") && !strings.HasPrefix(id, "oc_") {
					return output.ErrValidation("invalid attendee id format %q: should start with 'ou_' or 'oc_'", id)
				}
			}
		}

		startInput := runtime.Str(flagStart)
		if startInput != "" {
			if _, err := common.ParseTime(startInput); err != nil {
				return output.ErrValidation("invalid start time: %v", err)
			}
		}

		endInput := runtime.Str(flagEnd)
		if endInput != "" {
			if _, err := common.ParseTime(endInput, "end"); err != nil {
				return output.ErrValidation("invalid end time: %v", err)
			}
		}

		excludeStr := runtime.Str(flagExclude)
		if excludeStr != "" {
			excludeStr = strings.TrimSpace(excludeStr)
			ranges := strings.Split(excludeStr, ",")
			for _, r := range ranges {
				r = strings.TrimSpace(r)
				if r == "" {
					continue
				}
				parts := strings.Split(r, "~")
				if len(parts) != 2 {
					return output.ErrValidation("invalid range format in --exclude: %q, expect start~end", r)
				}
				if _, err := common.ParseTime(parts[0]); err != nil {
					return output.ErrValidation("invalid start time in --exclude: %q (%v)", parts[0], err)
				}
				if _, err := common.ParseTime(parts[1], "end"); err != nil {
					return output.ErrValidation("invalid end time in --exclude: %q (%v)", parts[1], err)
				}
			}
		}

		return nil
	},
	Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
		req, err := buildSuggestionRequest(runtime)
		if err != nil {
			return err
		}

		apiResp, err := runtime.DoAPI(&larkcore.ApiReq{
			HttpMethod: "POST",
			ApiPath:    suggestionPath,
			Body:       req,
		})
		if err != nil {
			return output.ErrWithHint(output.ExitInternal, "request_fail", "api request fail", err.Error())
		}

		if apiResp.StatusCode < http.StatusOK || apiResp.StatusCode >= http.StatusMultipleChoices {
			return output.ErrAPI(apiResp.StatusCode, "", string(apiResp.RawBody))
		}

		var resp = &OpenAPIResponse[*SuggestionResponse]{}
		if err := json.Unmarshal(apiResp.RawBody, &resp); err != nil {
			return output.ErrWithHint(output.ExitInternal, "validation", "unmarshal response fail", err.Error())
		}

		if resp.Code != 0 {
			return output.ErrAPI(resp.Code, resp.Msg, resp.Data)
		}

		data := resp.Data
		var suggestions []*EventTime
		var aiGuidance string
		if data != nil {
			suggestions = data.Suggestions
			aiGuidance = data.AiActionGuidance
		}
		runtime.OutFormat(data, &output.Meta{Count: len(suggestions)}, func(w io.Writer) {
			if len(suggestions) == 0 {
				fmt.Fprintln(w, "No suggestions available.")
			} else {
				var rows []map[string]interface{}
				for _, item := range suggestions {
					rows = append(rows, map[string]interface{}{
						"start":  item.EventStartTime,
						"end":    item.EventEndTime,
						"reason": item.RecommendReason,
					})
				}
				output.PrintTable(w, rows)
				fmt.Fprintf(w, "\n%d suggestion(s) found\n", len(suggestions))
			}

			if aiGuidance != "" {
				fmt.Fprintf(w, "\nAction Guidance: %s\n", aiGuidance)
			}
		})
		return nil
	},
}

Functions

func Shortcuts

func Shortcuts() []common.Shortcut

Shortcuts returns all calendar shortcuts.

Types

type EventTime

type EventTime struct {
	EventStartTime  string `json:"event_start_time,omitempty"`
	EventEndTime    string `json:"event_end_time,omitempty"`
	RecommendReason string `json:"recommend_reason,omitempty"`
}

type OpenAPIResponse

type OpenAPIResponse[T any] struct {
	Code int    `json:"code,omitempty"`
	Msg  string `json:"msg,omitempty"`
	Data T      `json:"data,omitempty"`
}

type SuggestionRequest

type SuggestionRequest struct {
	SearchStartTime    string       `json:"search_start_time,omitempty"`
	SearchEndTime      string       `json:"search_end_time,omitempty"`
	Timezone           string       `json:"timezone,omitempty"`
	EventRrule         string       `json:"event_rrule,omitempty"`
	DurationMinutes    int          `json:"duration_minutes,omitempty"`
	AttendeeUserIds    []string     `json:"attendee_user_ids,omitempty"`
	AttendeeChatIds    []string     `json:"attendee_chat_ids,omitempty"`
	ExcludedEventTimes []*EventTime `json:"excluded_event_times,omitempty"`
}

type SuggestionResponse

type SuggestionResponse struct {
	Suggestions      []*EventTime `json:"suggestions,omitempty"`
	AiActionGuidance string       `json:"ai_action_guidance,omitempty"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL