Skip to content

Commit b517dc0

Browse files
davidguttmanonutc
andauthored
feat(discord): add autoArchiveDuration config option (openclaw#35065)
* feat(discord): add autoArchiveDuration config option Add config option to control auto-archive duration for auto-created threads: - autoArchiveDuration: 60 (default), 1440, 4320, or 10080 - Sets archive duration in minutes (1hr/1day/3days/1week) - Accepts both string and numeric values - Discord's default was 60 minutes (hardcoded) Example config: ```yaml channels: discord: guilds: GUILD_ID: channels: CHANNEL_ID: autoThread: true autoArchiveDuration: 10080 # 1 week ``` * feat(discord): add autoArchiveDuration changelog entry (openclaw#35065) (thanks @davidguttman) --------- Co-authored-by: Onur <[email protected]>
1 parent a76e810 commit b517dc0

File tree

5 files changed

+93
-2
lines changed

5 files changed

+93
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Docs: https://docs.openclaw.ai
1212
- Exec/child commands: mark child command environments with `OPENCLAW_CLI` so subprocesses can detect when they were launched from the OpenClaw CLI. (#41411) Thanks @vincentkoc.
1313
- iOS/Home canvas: add a bundled welcome screen with a live agent overview that refreshes on connect, reconnect, and foreground return, and move the compact connection pill off the top-left canvas overlay. (#42456) Thanks @ngutman.
1414
- iOS/Home canvas: replace floating controls with a docked toolbar, make the bundled home scaffold adapt to smaller phones, and open chat in the resolved main session instead of a synthetic `ios` session. (#42456) Thanks @ngutman.
15+
- Discord/auto threads: add `autoArchiveDuration` channel config for auto-created threads so Discord thread archiving can stay at 1 hour, 1 day, 3 days, or 1 week instead of always using the 1-hour default. (#35065) Thanks @davidguttman.
1516

1617
### Breaking
1718

src/config/zod-schema.providers-core.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,16 @@ export const DiscordGuildChannelSchema = z
384384
systemPrompt: z.string().optional(),
385385
includeThreadStarter: z.boolean().optional(),
386386
autoThread: z.boolean().optional(),
387+
/** Archive duration for auto-created threads in minutes. Discord supports 60, 1440 (1 day), 4320 (3 days), 10080 (1 week). Default: 60. */
388+
autoArchiveDuration: z
389+
.union([
390+
z.enum(["60", "1440", "4320", "10080"]),
391+
z.literal(60),
392+
z.literal(1440),
393+
z.literal(4320),
394+
z.literal(10080),
395+
])
396+
.optional(),
387397
})
388398
.strict();
389399

src/discord/monitor/allow-list.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export type DiscordGuildEntryResolved = {
4040
systemPrompt?: string;
4141
includeThreadStarter?: boolean;
4242
autoThread?: boolean;
43+
autoArchiveDuration?: "60" | "1440" | "4320" | "10080" | 60 | 1440 | 4320 | 10080;
4344
}
4445
>;
4546
};
@@ -55,6 +56,7 @@ export type DiscordChannelConfigResolved = {
5556
systemPrompt?: string;
5657
includeThreadStarter?: boolean;
5758
autoThread?: boolean;
59+
autoArchiveDuration?: "60" | "1440" | "4320" | "10080" | 60 | 1440 | 4320 | 10080;
5860
matchKey?: string;
5961
matchSource?: ChannelMatchSource;
6062
};
@@ -401,6 +403,7 @@ function resolveDiscordChannelConfigEntry(
401403
systemPrompt: entry.systemPrompt,
402404
includeThreadStarter: entry.includeThreadStarter,
403405
autoThread: entry.autoThread,
406+
autoArchiveDuration: entry.autoArchiveDuration,
404407
};
405408
return resolved;
406409
}

src/discord/monitor/threading.auto-thread.test.ts

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ChannelType } from "@buape/carbon";
2-
import { describe, it, expect, vi } from "vitest";
2+
import { describe, it, expect, vi, beforeEach } from "vitest";
33
import { maybeCreateDiscordAutoThread } from "./threading.js";
44

55
describe("maybeCreateDiscordAutoThread", () => {
@@ -89,3 +89,74 @@ describe("maybeCreateDiscordAutoThread", () => {
8989
expect(postMock).toHaveBeenCalled();
9090
});
9191
});
92+
93+
describe("maybeCreateDiscordAutoThread autoArchiveDuration", () => {
94+
const postMock = vi.fn();
95+
const getMock = vi.fn();
96+
const mockClient = {
97+
rest: { post: postMock, get: getMock },
98+
} as unknown as Parameters<typeof maybeCreateDiscordAutoThread>[0]["client"];
99+
const mockMessage = {
100+
id: "msg1",
101+
timestamp: "123",
102+
} as unknown as Parameters<typeof maybeCreateDiscordAutoThread>[0]["message"];
103+
104+
beforeEach(() => {
105+
postMock.mockReset();
106+
getMock.mockReset();
107+
});
108+
109+
it("uses configured autoArchiveDuration", async () => {
110+
postMock.mockResolvedValueOnce({ id: "thread1" });
111+
await maybeCreateDiscordAutoThread({
112+
client: mockClient,
113+
message: mockMessage,
114+
messageChannelId: "text1",
115+
isGuildMessage: true,
116+
channelConfig: { allowed: true, autoThread: true, autoArchiveDuration: "10080" },
117+
channelType: ChannelType.GuildText,
118+
baseText: "test",
119+
combinedBody: "test",
120+
});
121+
expect(postMock).toHaveBeenCalledWith(
122+
expect.any(String),
123+
expect.objectContaining({ body: expect.objectContaining({ auto_archive_duration: 10080 }) }),
124+
);
125+
});
126+
127+
it("accepts numeric autoArchiveDuration", async () => {
128+
postMock.mockResolvedValueOnce({ id: "thread1" });
129+
await maybeCreateDiscordAutoThread({
130+
client: mockClient,
131+
message: mockMessage,
132+
messageChannelId: "text1",
133+
isGuildMessage: true,
134+
channelConfig: { allowed: true, autoThread: true, autoArchiveDuration: 4320 },
135+
channelType: ChannelType.GuildText,
136+
baseText: "test",
137+
combinedBody: "test",
138+
});
139+
expect(postMock).toHaveBeenCalledWith(
140+
expect.any(String),
141+
expect.objectContaining({ body: expect.objectContaining({ auto_archive_duration: 4320 }) }),
142+
);
143+
});
144+
145+
it("defaults to 60 when autoArchiveDuration not set", async () => {
146+
postMock.mockResolvedValueOnce({ id: "thread1" });
147+
await maybeCreateDiscordAutoThread({
148+
client: mockClient,
149+
message: mockMessage,
150+
messageChannelId: "text1",
151+
isGuildMessage: true,
152+
channelConfig: { allowed: true, autoThread: true },
153+
channelType: ChannelType.GuildText,
154+
baseText: "test",
155+
combinedBody: "test",
156+
});
157+
expect(postMock).toHaveBeenCalledWith(
158+
expect.any(String),
159+
expect.objectContaining({ body: expect.objectContaining({ auto_archive_duration: 60 }) }),
160+
);
161+
});
162+
});

src/discord/monitor/threading.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,12 +397,18 @@ export async function maybeCreateDiscordAutoThread(params: {
397397
params.baseText || params.combinedBody || "Thread",
398398
params.message.id,
399399
);
400+
401+
// Parse archive duration from config, default to 60 minutes
402+
const archiveDuration = params.channelConfig?.autoArchiveDuration
403+
? Number(params.channelConfig.autoArchiveDuration)
404+
: 60;
405+
400406
const created = (await params.client.rest.post(
401407
`${Routes.channelMessage(messageChannelId, params.message.id)}/threads`,
402408
{
403409
body: {
404410
name: threadName,
405-
auto_archive_duration: 60,
411+
auto_archive_duration: archiveDuration,
406412
},
407413
},
408414
)) as { id?: string };

0 commit comments

Comments
 (0)