Skip to content

Commit e924f16

Browse files
committed
fixes and adjustments to avoid rate-limit and other weird notion issues
1 parent 225c85d commit e924f16

File tree

6 files changed

+71
-12
lines changed

6 files changed

+71
-12
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ The following configurations can be set in the env of your project, in order to
6666
- `DRY_RUN` - set to `1` if you wish to just test the create/update/delete plan of this bot, without affecting any data on GitHub.
6767
- `ENABLE_FETCH` - Set to `1` to enable. This will enable the `fetch` event for the worker, this is helpful for development if you want to trigger the bot manually, or if you wish your bot to have a manual trigger.
6868
- `CUSTOM_HEADER_LINK` - customize the link added to the header of every GitHub issue/discussion. To use an external like, you can add markdown, for example: `[The Guild's](https://the-guild.dev)`.
69+
- `IGNORED_REPOS` - If the GitHub user you are using, is used for creating other issues/discussions, or a real (non-bot) user, you can reduce the stress of loading existing issues/discussion with ignoring some repos. This field is comma-separated (for example: `user/repo1,user/repo2`)
6970

7071
For local development, please add your config to a file called `.dev.vars`
7172

src/index.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@ export interface Env {
2121
DRY_RUN?: string;
2222
ENABLE_FETCH?: string;
2323
CUSTOM_HEADER_LINK?: string;
24+
IGNORED_REPOS?: string;
2425
}
2526

2627
async function run(env: Env) {
28+
const ignoredRepos = (env.IGNORED_REPOS || "")
29+
.split(",")
30+
.map((v) => v.trim());
2731
const shouldExecute = !env.DRY_RUN;
2832
const botBrand = env.CUSTOM_HEADER_LINK ? `${env.CUSTOM_HEADER_LINK} ` : "";
2933
const headerNote = `> This page is synced automatically from ${botBrand}Notion`;
@@ -34,12 +38,15 @@ async function run(env: Env) {
3438
const n2m = new NotionToMarkdown({ notionClient: notion });
3539
const octokit = new Octokit({ auth: env.GH_BOT_TOKEN });
3640
const login = await getBotLogin(octokit);
41+
console.info(`GitHub user identified as: ${login}`);
3742
const [relevantPages, discussions, issues] = await Promise.all([
3843
getSharedNotionPages(notion),
39-
getExistingDiscussions(octokit, login),
40-
getExistingIssues(octokit, login),
44+
getExistingDiscussions(octokit, login, ignoredRepos),
45+
getExistingIssues(octokit, login, ignoredRepos),
4146
]);
42-
console.log("existing issues found:", issues);
47+
console.info("Found existing issues:", issues);
48+
console.info("Found existing discussions:", discussions);
49+
console.info("Found shared Notion pages:", relevantPages);
4350
const { discussions: discussionsPlan, issues: issuesPlan } =
4451
await buildUpdatePlan(
4552
octokit,
@@ -50,8 +57,8 @@ async function run(env: Env) {
5057
headerNote
5158
);
5259

53-
console.info(`Built Discussion sync plan:`, discussionsPlan);
54-
console.info(`Built Issues sync plan:`, issuesPlan);
60+
console.info(`Built GitHub Discussion sync plan:`, discussionsPlan);
61+
console.info(`Built GitHub Issues sync plan:`, issuesPlan);
5562

5663
await Promise.all([
5764
...discussionsPlan.delete.map(async (item) => {

src/notion-helpers.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Client } from "@notionhq/client";
1+
import { Client, isFullPage } from "@notionhq/client";
22
import { Page } from "./utils";
33

44
export function composeLink(page: Page): string {
@@ -38,7 +38,35 @@ export function extractPageTitle(page: Page): string | null {
3838
export async function getSharedNotionPages(notion: Client) {
3939
const relevantPages = await notion.search({
4040
page_size: 100,
41+
filter: {
42+
property: "object",
43+
value: "page",
44+
},
45+
sort: {
46+
direction: "descending",
47+
timestamp: "last_edited_time",
48+
},
4149
});
4250

4351
return relevantPages.results;
4452
}
53+
54+
export function shouldHandlePage(page: any): boolean {
55+
if (isFullPage(page)) {
56+
if (page.archived) {
57+
return false;
58+
}
59+
60+
// These are usually created by Notion when you create a DB inside Page.
61+
// In most cases, these pages are empty and causes the bot to overfetch, until it gets rate limited.
62+
const isMultiSelect = page.properties?.Type?.type === "multi_select";
63+
64+
if (isMultiSelect) {
65+
return false;
66+
}
67+
68+
return true;
69+
}
70+
71+
return false;
72+
}

src/plan.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
composeLink,
66
composeSignature,
77
extractPageTitle,
8+
shouldHandlePage,
89
} from "./notion-helpers";
910
import {
1011
Discussion,
@@ -107,10 +108,21 @@ export async function buildUpdatePlan(
107108
const pageTitle = extractPageTitle(page);
108109

109110
if (!pageTitle) {
111+
console.warn(`Notion object with id ${page.id} has no title, skipping`);
112+
113+
return;
114+
}
115+
116+
const shouldHandle = shouldHandlePage(page);
117+
118+
if (!shouldHandle) {
119+
console.warn(
120+
`Notion object with title ${pageTitle} is not a syncable page, skipping`
121+
);
122+
110123
return;
111124
}
112125

113-
console.info(`Building plan for page: `, pageTitle, page);
114126
const mdBlocks = await n2m.pageToMarkdown(page.id, 2);
115127
const pageAttributes = distinguishPage(mdBlocks[0]);
116128
const notionPageSignature = composeSignature(page.id);
@@ -122,6 +134,8 @@ export async function buildUpdatePlan(
122134
);
123135

124136
if (pageAttributes === null) {
137+
console.warn(`Notion page with title "${pageTitle}" has no attributes`);
138+
125139
if (existingDiscussion) {
126140
outputDiscussions.delete.push({
127141
repoId: existingDiscussion.repository.id,

src/utils.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ function isDiscussion(obj: DiscussionsSearchResult): obj is Discussion {
3131
return obj?.__typename === "Discussion";
3232
}
3333

34-
export async function getExistingDiscussions(octokit: Octokit, login: string) {
34+
export async function getExistingDiscussions(
35+
octokit: Octokit,
36+
login: string,
37+
ignoredRepos: string[] = []
38+
) {
3539
const discussionsByBot = await octokit.graphql<MyDiscussionsQuery>(
3640
/* GraphQL */ `
3741
query myDiscussions($q: String!) {
@@ -58,7 +62,7 @@ export async function getExistingDiscussions(octokit: Octokit, login: string) {
5862
}
5963
`,
6064
{
61-
q: `author:${login} -repo:the-guild-org/crisp-chats`,
65+
q: `author:${login} ${ignoredRepos.map((v) => `-repo:${v}`).join(" ")}`,
6266
}
6367
);
6468

@@ -69,7 +73,11 @@ function isIssue(obj: IssuesSearchResult): obj is Issue {
6973
return obj?.__typename === "Issue";
7074
}
7175

72-
export async function getExistingIssues(octokit: Octokit, login: string) {
76+
export async function getExistingIssues(
77+
octokit: Octokit,
78+
login: string,
79+
ignoredRepos: string[]
80+
) {
7381
const issuesByBot = await octokit.graphql<MyIssuesQuery>(
7482
/* GraphQL */ `
7583
query myIssues($q: String!) {
@@ -96,7 +104,7 @@ export async function getExistingIssues(octokit: Octokit, login: string) {
96104
}
97105
`,
98106
{
99-
q: `author:${login} -repo:the-guild-org/crisp-chats`,
107+
q: `author:${login} ${ignoredRepos.map((v) => `-repo:${v}`).join(" ")}`,
100108
}
101109
);
102110

wrangler.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ crons = ["*/5 * * * *"] # every 5 minutes
77

88
[vars]
99
CUSTOM_HEADER_LINK = "[The Guild's](https://the-guild.dev)"
10-
ENABLE_FETCH = "1"
10+
ENABLE_FETCH = "1"
11+
IGNORED_REPOS = "the-guild-org/crisp-chats"

0 commit comments

Comments
 (0)