mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 07:01:24 +00:00
discord: auto-create thread when sending to Forum/Media channels (#12380)
* discord: auto-create thread when sending to Forum/Media channels * Discord: harden forum thread sends (#12380) (thanks @magendary) * fix: clean up discord send exports (#12380) (thanks @magendary) --------- Co-authored-by: Shadow <shadow@clawd.bot>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { PermissionFlagsBits, Routes } from "discord-api-types/v10";
|
||||
import { ChannelType, PermissionFlagsBits, Routes } from "discord-api-types/v10";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
deleteMessageDiscord,
|
||||
@@ -58,7 +58,9 @@ describe("sendMessageDiscord", () => {
|
||||
});
|
||||
|
||||
it("sends basic channel messages", async () => {
|
||||
const { rest, postMock } = makeRest();
|
||||
const { rest, postMock, getMock } = makeRest();
|
||||
// Channel type lookup returns a normal text channel (not a forum).
|
||||
getMock.mockResolvedValueOnce({ type: ChannelType.GuildText });
|
||||
postMock.mockResolvedValue({
|
||||
id: "msg1",
|
||||
channel_id: "789",
|
||||
@@ -74,6 +76,89 @@ describe("sendMessageDiscord", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("auto-creates a forum thread when target is a Forum channel", async () => {
|
||||
const { rest, postMock, getMock } = makeRest();
|
||||
// Channel type lookup returns a Forum channel.
|
||||
getMock.mockResolvedValueOnce({ type: ChannelType.GuildForum });
|
||||
postMock.mockResolvedValue({
|
||||
id: "thread1",
|
||||
message: { id: "starter1", channel_id: "thread1" },
|
||||
});
|
||||
const res = await sendMessageDiscord("channel:forum1", "Discussion topic\nBody of the post", {
|
||||
rest,
|
||||
token: "t",
|
||||
});
|
||||
expect(res).toEqual({ messageId: "starter1", channelId: "thread1" });
|
||||
// Should POST to threads route, not channelMessages.
|
||||
expect(postMock).toHaveBeenCalledWith(
|
||||
Routes.threads("forum1"),
|
||||
expect.objectContaining({
|
||||
body: {
|
||||
name: "Discussion topic",
|
||||
message: { content: "Discussion topic\nBody of the post" },
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("posts media as a follow-up message in forum channels", async () => {
|
||||
const { rest, postMock, getMock } = makeRest();
|
||||
getMock.mockResolvedValueOnce({ type: ChannelType.GuildForum });
|
||||
postMock
|
||||
.mockResolvedValueOnce({
|
||||
id: "thread1",
|
||||
message: { id: "starter1", channel_id: "thread1" },
|
||||
})
|
||||
.mockResolvedValueOnce({ id: "media1", channel_id: "thread1" });
|
||||
const res = await sendMessageDiscord("channel:forum1", "Topic", {
|
||||
rest,
|
||||
token: "t",
|
||||
mediaUrl: "file:///tmp/photo.jpg",
|
||||
});
|
||||
expect(res).toEqual({ messageId: "starter1", channelId: "thread1" });
|
||||
expect(postMock).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
Routes.threads("forum1"),
|
||||
expect.objectContaining({
|
||||
body: {
|
||||
name: "Topic",
|
||||
message: { content: "Topic" },
|
||||
},
|
||||
}),
|
||||
);
|
||||
expect(postMock).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
Routes.channelMessages("thread1"),
|
||||
expect.objectContaining({
|
||||
body: expect.objectContaining({
|
||||
files: [expect.objectContaining({ name: "photo.jpg" })],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("chunks long forum posts into follow-up messages", async () => {
|
||||
const { rest, postMock, getMock } = makeRest();
|
||||
getMock.mockResolvedValueOnce({ type: ChannelType.GuildForum });
|
||||
postMock
|
||||
.mockResolvedValueOnce({
|
||||
id: "thread1",
|
||||
message: { id: "starter1", channel_id: "thread1" },
|
||||
})
|
||||
.mockResolvedValueOnce({ id: "msg2", channel_id: "thread1" });
|
||||
const longText = "a".repeat(2001);
|
||||
await sendMessageDiscord("channel:forum1", longText, {
|
||||
rest,
|
||||
token: "t",
|
||||
});
|
||||
const firstBody = postMock.mock.calls[0]?.[1]?.body as {
|
||||
message?: { content?: string };
|
||||
};
|
||||
const secondBody = postMock.mock.calls[1]?.[1]?.body as { content?: string };
|
||||
expect(firstBody?.message?.content).toHaveLength(2000);
|
||||
expect(secondBody?.content).toBe("a");
|
||||
});
|
||||
|
||||
it("starts DM when recipient is a user", async () => {
|
||||
const { rest, postMock } = makeRest();
|
||||
postMock
|
||||
@@ -118,6 +203,7 @@ describe("sendMessageDiscord", () => {
|
||||
});
|
||||
postMock.mockRejectedValueOnce(apiError);
|
||||
getMock
|
||||
.mockResolvedValueOnce({ type: ChannelType.GuildText })
|
||||
.mockResolvedValueOnce({
|
||||
id: "789",
|
||||
guild_id: "guild1",
|
||||
|
||||
Reference in New Issue
Block a user