The official Go SDK for the hduhelp-neo API, modeled on the Feishu
(larksuite/oapi-sdk-go) ergonomics: a namespaced client, fluent per-endpoint
request builders, automatic token management, and typed response wrappers.
client := hduhelp.NewClient(appID, appSecret)
req := academic.NewScheduleReqBuilder().
SchoolYear("2025-2026").
Semester(1).
Build()
resp, err := client.Academic.Schedule(ctx, req)
if err != nil {
return err
}
if !resp.Success() {
log.Println(resp.Code, resp.Msg, resp.RequestID())
return
}
use(resp.Data) // []models.ScheduleItemgo get github.com/hduhelp/hduhelp-neo-sdk-go@latestRequires Go 1.24+.
The SDK is generated from the API's openapi.yaml in two layers:
- Models (
modelspackage) — plain typed structs for every schema, generated byoapi-codegen. - Services (
service/<name>packages) plus the top-levelClient— the Feishu-style layer, generated by thecmd/hduhelp-sdk-gengenerator in the upstream hduhelp-neo repo: one service per API tag, a fluent request builder per endpoint, and a typed response wrapper per endpoint.
Auth and transport live in the hand-written core package.
NewClient(appID, appSecret, ...ClientOption) works zero-config; options tune
the rest:
| Option | Effect |
|---|---|
hduhelp.WithBaseURL(url) |
Gateway base URL (default https://api.hduhelp.com). |
hduhelp.WithHTTPClient(hc) |
Supply your own *http.Client. |
hduhelp.WithReqTimeout(d) |
Per-request timeout for the built-in client (default 30s). |
hduhelp.WithEnableTokenCache(b) |
Toggle automatic token management (default on). |
hduhelp.WithPAT(token) |
Authenticate every call with a personal access token. |
hduhelp.WithLogLevel(l) |
Log verbosity. |
With app credentials the client fetches, caches, and auto-refreshes the
tenant_access_token (via /hduhelp-neo/open-apis/auth/tenant-access-token/internal)
and injects it as Authorization: Bearer <token>. You never touch the token
endpoint.
client := hduhelp.NewClient("cli_xxx", "secret")
// StaffID is required when the automatic tenant token targets a user.
req := academic.NewStudentInfoReqBuilder().
StaffID("2025123456").
Build()
resp, err := client.Academic.StudentInfo(ctx, req)client := hduhelp.NewClient("", "", hduhelp.WithPAT("hduhelp_pat_xxx"))Pass a trailing option to any call to override the client default for that call (precedence: user > tenant > PAT):
resp, err := client.Academic.Schedule(ctx, req, hduhelp.WithUserAccessToken(uat))
resp, err = client.Academic.Schedule(ctx, req, hduhelp.WithTenantAccessToken(tat))
resp, err = client.Academic.Schedule(ctx, req, hduhelp.WithPAT(pat))Build an authorize URL with an S256 PKCE challenge, redirect the user, then
exchange the returned code with the saved code_verifier. UserTokenSource
auto-refreshes and rotates the refresh token. RedirectURI is optional: when
omitted, Neo uses the first redirect URI registered for the application. The
authorization-code exchange follows Neo's form-encoded token contract.
auth := client.UserAuth()
pkce, _ := hduhelp.GeneratePKCE() // keep pkce.Verifier
url, _ := auth.AuthorizeURL(hduhelp.AuthorizeParams{
RedirectURI: "https://app.example.com/callback",
Scope: "contact:user.id:read",
State: "xyz",
PKCE: pkce,
})
// ... redirect the user to url; on callback you receive `code` ...
tok, _ := auth.ExchangeCode(ctx, code, pkce.Verifier)
src := hduhelp.NewUserTokenSource(auth, tok)
uat, _ := src.Token(ctx) // valid access token, refreshed as needed
resp, err := client.Academic.Schedule(ctx, req, hduhelp.WithUserAccessToken(uat))Each endpoint has New<Method>ReqBuilder() with a setter per query, path, and
header parameter, a .Body(*models.X) setter when the endpoint takes a body,
and .Build(). For a user-data endpoint called with a tenant/app token, set
the generated .StaffID(...) header parameter; a user token gets the target
identity from the token and does not need it. Each call returns a typed
*<Method>Resp embedding:
resp.Success()— true when the business code is 0.resp.Code,resp.Msg— the response envelope.resp.RequestID()— the server request id, for tracing.resp.Data— the typed payload (*models.X,[]models.X, or a scalar).resp.StatusCode,resp.Header,resp.RawBody— the raw transport result.
Services: client.Academic, client.Admin, client.AdminNotice,
client.CampusLife, client.EmptySchedule, client.Feed, client.Graduate,
client.GroupChat, client.Health, client.Identity, client.Inbox,
client.Knowledge, client.LibraryBooking, client.Messaging,
client.Notification, client.SiteAnnouncement, client.Subscription, and
client.Upload.
Generation is owned by the upstream
hduhelp-neo repo: the OpenAPI spec,
the spec normalizer, the oapi-codegen config, and the cmd/hduhelp-sdk-gen
generator all live there. To change the SDK surface, change the API upstream;
its release pipeline regenerates from swagger/openapi.yaml, builds and tests
the result, and pushes the generated client and tag here. The generated files
(models, service, client.gen.go) and the vendored openapi.yaml are not
edited in this repo by hand.
Generated SDK releases are driven from the upstream hduhelp-neo repository.
Bump the backend key in its release.yml and push to main: the backend
version also drives the Go SDK version. That pipeline clones this repository's
current main, regenerates the SDK from the upstream swagger/openapi.yaml,
commits any generated-file changes, and creates the annotated tag v<version>.
Tagging is the entire publish step for a Go module — consumers then run
go get github.com/hduhelp/hduhelp-neo-sdk-go@v<version>.
The hand-written core, tests, and documentation are maintained in this
repository. The vendored openapi.yaml, generated files, versions, and tags are
owned by the upstream pipeline; do not edit those generated artifacts here by
hand.
MIT. See LICENSE.