Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ auto-registration、token lifecycle、request signature verification を担当

## このアプリで確認できること

- `github.com/channel-io/app-sdk/go` `v0.14.0`
- `go.mod` に固定された `github.com/channel-io/app-sdk/go` version
- SDK builder で登録する `command` Extension
- Typed app Function と生成される JSON Schema
- SDK-managed app/channel token cache と refresh
Expand All @@ -39,10 +39,9 @@ silent close せず error state を表示します。
- **WAM**: React UI は `/resource/wam/tutorial` で配信します。`useCallFunction` は app server、`useNativeFunction` は現在の manager として Channel を呼びます。
- **認証**: SDK server が inbound signature を検証し、`native.TokenManager` が bot path の channel token を cache します。Server は許可済み group-chat target に短期 signature を付けて WAM に渡し、manager authorization は Channel host が管理します。

Go SDK は token lifecycle を管理しますが、まだ `writeGroupMessage` typed proxy wrapper を
提供していません。`internal/tutorial/native_message.go` はこの native Function だけを分離した
transport adapter です。その他の token repository、command registrar、Function router は SDK
が担当します。
Bot path は `native.TokenManager` で channel token を取得し、
`native.ProxyAPI.WriteGroupMessage` を呼びます。App は Native Function HTTP transport を直接
実装しません。

## SDK contract

Expand Down Expand Up @@ -142,12 +141,12 @@ cmd/main.go SDK server と Extension auto-registration
cmd/function_endpoint.go bare Function Endpoint compatibility route
internal/tutorial/app.go command metadata と typed app Function
internal/tutorial/contracts.go public WAM contract の Go type と name
internal/tutorial/native_message.go 1 つの native transport adapter
internal/tutorial/native_message.go SDK token manager と typed ProxyAPI call
contracts/ language-neutral WAM wire schema
wam/src/contracts.ts TypeScript view と runtime validation
wam/src/pages/Send/Send.tsx app/native call 用 WAM SDK hook
```

現在の contract は SDK guide と reference で確認し、完全な Go server/WAM implementation は
この repository で確認してください。SDK Quickstart もこの tutorial を参照し、Go native
transport gap は feature-parity 文書で明記します
この repository で確認してください。SDK Quickstart もこの tutorial を runnable Go example
として参照します
13 changes: 6 additions & 7 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ auto-registration, token lifecycle, request signature 검증을 담당합니다.

## 이 앱에서 확인할 수 있는 것

- `github.com/channel-io/app-sdk/go` `v0.14.0`
- `go.mod`에 고정된 `github.com/channel-io/app-sdk/go` 버전
- SDK builder로 등록하는 `command` Extension
- Typed app Function과 생성되는 JSON Schema
- SDK가 관리하는 app/channel token cache와 refresh
Expand All @@ -39,9 +39,9 @@ manager 권한으로 team-chat message를 보냅니다. 지원하지 않는 chat
- **WAM**: React UI는 `/resource/wam/tutorial`에서 제공됩니다. `useCallFunction`은 app server를, `useNativeFunction`은 현재 manager 주체로 Channel을 호출합니다.
- **인증**: SDK server가 inbound signature를 검증하고 `native.TokenManager`가 bot 경로의 channel token을 cache합니다. Server는 허용된 group-chat target에 짧은 signature를 붙여 WAM에 전달하며 manager authorization은 Channel host가 관리합니다.

Go SDK는 token lifecycle을 관리하지만 아직 `writeGroupMessage` typed proxy wrapper를 제공하지
않습니다. `internal/tutorial/native_message.go`는 이 native Function 하나만 격리한 transport
adapter입니다. 나머지 token repository, command registrar, Function router는 SDK가 담당합니다.
Bot 경로는 `native.TokenManager`로 channel token을 얻고
`native.ProxyAPI.WriteGroupMessage`를 호출합니다. 앱이 Native Function HTTP transport를 직접
구현하지 않습니다.

## SDK 계약

Expand Down Expand Up @@ -140,12 +140,11 @@ cmd/main.go SDK server와 Extension auto-registration
cmd/function_endpoint.go bare Function Endpoint compatibility route
internal/tutorial/app.go command metadata와 typed app Function
internal/tutorial/contracts.go public WAM contract의 Go type과 name
internal/tutorial/native_message.go 하나의 native transport adapter
internal/tutorial/native_message.go SDK token manager와 typed ProxyAPI 호출
contracts/ 언어 중립 WAM wire schema
wam/src/contracts.ts TypeScript view와 runtime validation
wam/src/pages/Send/Send.tsx app/native call용 WAM SDK hook
```

현재 계약은 SDK guide와 reference에서 확인하고, 완성된 Go server/WAM 구현은 이 저장소에서
확인하세요. SDK Quickstart도 이 튜토리얼을 연결하며 Go native transport gap은 기능 동등성
문서에서 명시합니다.
확인하세요. SDK Quickstart도 이 튜토리얼을 실행 가능한 Go 예제로 연결합니다.
13 changes: 5 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ design guidance:

## What this app demonstrates

- `github.com/channel-io/app-sdk/go` `v0.14.0`
- the `github.com/channel-io/app-sdk/go` version pinned in `go.mod`
- a `command` extension registered through the SDK builder
- typed app functions and generated JSON schemas
- SDK-managed app/channel token caching and refresh
Expand All @@ -47,10 +47,8 @@ Concepts in this repository map to concrete code as follows:
- **WAM**: the React UI is served at `/resource/wam/tutorial`; `useCallFunction` calls the app server and `useNativeFunction` acts as the current manager.
- **Authentication**: the SDK server verifies inbound signatures, `native.TokenManager` caches the channel token used by the bot path, the server signs the allowed group-chat target before giving it to the WAM, and the Channel host owns manager authorization.

The Go SDK currently owns the token lifecycle but does not yet expose a typed proxy wrapper for
`writeGroupMessage`. `internal/tutorial/native_message.go` is therefore a deliberately small
transport adapter for that one native function; the rest of the old hand-written AppStore client,
token repository, command registrar, and function router has been removed.
The bot path obtains a channel token through `native.TokenManager` and calls
`native.ProxyAPI.WriteGroupMessage`; the app does not implement Native Function HTTP transport.

## SDK contract alignment

Expand Down Expand Up @@ -151,12 +149,11 @@ cmd/main.go SDK server and extension auto-registration
cmd/function_endpoint.go bare Function Endpoint compatibility route
internal/tutorial/app.go command metadata and typed app functions
internal/tutorial/contracts.go Go types and names for the public WAM contract
internal/tutorial/native_message.go one native transport adapter
internal/tutorial/native_message.go SDK token manager and typed ProxyAPI call
contracts/ language-neutral WAM wire schema
wam/src/contracts.ts TypeScript view and runtime validation of that schema
wam/src/pages/Send/Send.tsx WAM SDK hooks for app/native calls
```

Use the SDK guides and references for the current contract, and use this repository for its complete
Go server-and-WAM implementation. The SDK quickstart links here and calls out the one native
transport gap explicitly.
Go server-and-WAM implementation. The SDK Quickstart links here as the runnable Go example.
2 changes: 1 addition & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func main() {
AppID: cfg.AppID,
AppSecret: cfg.AppSecret,
BotName: cfg.BotName,
}, tutorial.NewNativeMessageSender(tokenManager, cfg.AppStoreURL))
}, tutorial.NewNativeMessageSender(tokenManager, nativeClient))
if err != nil {
log.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/channel-io/app-tutorial
go 1.25.0

require (
github.com/channel-io/app-sdk/go v0.14.0
github.com/channel-io/app-sdk/go v0.14.1-0.20260723153640-6dfd90dad8a1
github.com/gin-gonic/gin v1.10.1
google.golang.org/protobuf v1.36.11
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/channel-io/app-sdk/go v0.14.0 h1:lmtHnhPXN3C4Pix+GGUk7v6tmpc1ThMz+kgEeIsVs9A=
github.com/channel-io/app-sdk/go v0.14.0/go.mod h1:A/ATvjAeGsrttLIW6hn3ot63MjZ/kUZDw1FCGdPVNNk=
github.com/channel-io/app-sdk/go v0.14.1-0.20260723153640-6dfd90dad8a1 h1:9UqsO45sjkfeWbHhsUDOXom1fQ9qU6fqfGwFxPOMB5s=
github.com/channel-io/app-sdk/go v0.14.1-0.20260723153640-6dfd90dad8a1/go.mod h1:A/ATvjAeGsrttLIW6hn3ot63MjZ/kUZDw1FCGdPVNNk=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
Expand Down
73 changes: 15 additions & 58 deletions internal/tutorial/native_message.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,19 @@
package tutorial

import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"

"github.com/channel-io/app-sdk/go/native"
)

type NativeMessageSender struct {
tokens *native.TokenManager
appStoreURL string
httpClient *http.Client
tokens *native.TokenManager
client *native.Client
}

func NewNativeMessageSender(tokens *native.TokenManager, appStoreURL string) *NativeMessageSender {
return &NativeMessageSender{
tokens: tokens,
appStoreURL: strings.TrimRight(appStoreURL, "/"),
httpClient: &http.Client{Timeout: 15 * time.Second},
}
func NewNativeMessageSender(tokens *native.TokenManager, client *native.Client) *NativeMessageSender {
return &NativeMessageSender{tokens: tokens, client: client}
}

func (s *NativeMessageSender) SendGroupMessage(
Expand All @@ -38,54 +28,21 @@ func (s *NativeMessageSender) SendGroupMessage(
return fmt.Errorf("get channel token: %w", err)
}

payload := map[string]any{
"method": "writeGroupMessage",
"params": map[string]any{
"channelId": channelID,
"groupId": groupID,
"rootMessageId": input.RootMessageID,
"broadcast": input.Broadcast,
"dto": map[string]any{
"plainText": message,
"botName": botName,
_, err = s.client.CreateProxyAPI(token.AccessToken).WriteGroupMessage(
ctx,
native.WriteGroupMessageParams{
ChannelID: channelID,
GroupID: groupID,
RootMessageID: input.RootMessageID,
Broadcast: input.Broadcast,
DTO: native.WriteMessageDTO{
PlainText: message,
BotName: botName,
},
},
}
body, err := json.Marshal(payload)
if err != nil {
return err
}
req, err := http.NewRequestWithContext(
ctx,
http.MethodPut,
s.appStoreURL+"/general/v1/native/functions",
bytes.NewReader(body),
)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-access-token", token.AccessToken)

resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("native function returned HTTP %d", resp.StatusCode)
}

var result struct {
Error *struct {
Message string `json:"message"`
} `json:"error,omitempty"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return err
}
if result.Error != nil {
return fmt.Errorf("native function error: %s", result.Error.Message)
return fmt.Errorf("write group message: %w", err)
}
return nil
}
2 changes: 1 addition & 1 deletion wam/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@channel.io/app-sdk-wam": "0.17.2",
"@channel.io/app-sdk-wam-ui": "0.4.0",
"@channel.io/bezier-icons": "0.60.0",
"@channel.io/bezier-react": "4.0.0-next.13",
"@channel.io/bezier-react": "4.0.0-next.14",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"styled-components": "^6.4.3"
Expand Down
10 changes: 5 additions & 5 deletions wam/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,9 @@ __metadata:
languageName: node
linkType: hard

"@channel.io/bezier-react@npm:4.0.0-next.13":
version: 4.0.0-next.13
resolution: "@channel.io/bezier-react@npm:4.0.0-next.13"
"@channel.io/bezier-react@npm:4.0.0-next.14":
version: 4.0.0-next.14
resolution: "@channel.io/bezier-react@npm:4.0.0-next.14"
dependencies:
"@channel.io/bezier-tokens": "npm:1.0.0-next.5"
"@radix-ui/react-checkbox": "npm:^1.1.3"
Expand All @@ -347,7 +347,7 @@ __metadata:
peerDependenciesMeta:
"@channel.io/bezier-icons":
optional: true
checksum: 10c0/edb77b9ae10c51f982e98ea78d4b7ccde4c0d3c411b4182734ee1474c1bab485f1ecbe9d3e891136698287dd23021472af5693e471500c314f69073dc436ad0d
checksum: 10c0/8414ac28827497b51803990daa5484f5a4d56e77d8a1a7944df4a84d357777e11dbc21f014404f62c89605af76e1b69a9770f0bbefdbd0d0416961244e1d84d8
languageName: node
linkType: hard

Expand Down Expand Up @@ -1907,7 +1907,7 @@ __metadata:
"@channel.io/app-sdk-wam": "npm:0.17.2"
"@channel.io/app-sdk-wam-ui": "npm:0.4.0"
"@channel.io/bezier-icons": "npm:0.60.0"
"@channel.io/bezier-react": "npm:4.0.0-next.13"
"@channel.io/bezier-react": "npm:4.0.0-next.14"
"@types/react": "npm:^18.2.56"
"@types/react-dom": "npm:^18.2.19"
"@typescript-eslint/eslint-plugin": "npm:^7.0.2"
Expand Down
Loading