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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Discord API
Cloudflare Worker ──────────────────────────────┐
│ │
│ GraphQL: isSponsoredBy(org)
│ GraphQL: sponsorshipsAsMaintainer(org) │
▼ │
GitHub API (Sponsors) │
│ │
Expand All @@ -36,7 +36,7 @@ GitHub API (Sponsors) │
| ------------- | ----------------------------------------------------------------------- |
| Runtime | Cloudflare Workers (TypeScript) |
| Slash Command | Discord Interactions API |
| 후원 확인 | GitHub GraphQL API (`isOrganizationSponsoredBy`) |
| 후원 확인 | GitHub GraphQL API (`sponsorshipsAsMaintainer`) |
| 팀 초대 | GitHub REST API (`PUT /orgs/{org}/teams/{team}/memberships/{username}`) |
| 역할 부여 | Discord REST API (`PUT /guilds/{guild}/members/{user}/roles/{role}`) |
| 배포 | Wrangler CLI |
Expand Down Expand Up @@ -145,7 +145,7 @@ wrangler deploy

`/verify` 실행 시 후원 조건을 아래 방식으로 검증합니다.

- GitHub GraphQL API로 해당 유저의 **전체 후원 기록**을 조회합니다.
- GitHub GraphQL API로 해당 유저의 **전체 후원 기록**을 조회합니다. (비공개 후원도 포함 — `sponsorshipsAsMaintainer(includePrivate: true)`)
- 조회한 기록 중 **팀 생성일 이후**의 기록만 필터링합니다.
- one-time 후원과 정기 후원을 구분하지 않고 **금액을 모두 합산**합니다.
- 합산 금액이 **$5 이상**이면 검증 통과입니다.
Expand Down
14 changes: 14 additions & 0 deletions src/github.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const mockEnv: Env = {
APP_PRIVATE_KEY: "",
ROLE_TEAM_CONFIG: "[]",
GH_PAT: "test-pat",
STUDY_JOIN_CHANNEL_ID: "test-channel-id",
};

function makeSponsorshipResponse(
Expand Down Expand Up @@ -157,6 +158,19 @@ describe("checkSponsorship", () => {
expect(url).toBe("https://api.github.com/graphql");
expect(options.headers["Authorization"]).toBe("Bearer my-token");
});

it("비공개 후원도 포함하도록 includePrivate: true로 요청한다", async () => {
const mockFetch = vi
.fn()
.mockResolvedValue(makeSponsorshipResponse([{ login: "octocat" }]));
vi.stubGlobal("fetch", mockFetch);

await checkSponsorship("octocat", "DaleStudy", "my-token");

const [, options] = mockFetch.mock.calls[0];
const body = JSON.parse(options.body);
expect(body.query).toContain("includePrivate: true");
});
});

describe("getTeamCreatedAt", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export async function checkSponsorship(
const query = `
query($org: String!, $cursor: String) {
organization(login: $org) {
sponsorshipsAsMaintainer(first: 100, after: $cursor, activeOnly: false) {
sponsorshipsAsMaintainer(first: 100, after: $cursor, activeOnly: false, includePrivate: true) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 필드가 있었군요!

nodes {
createdAt
isOneTimePayment
Expand Down
Loading