我现在要求你现在作为一个经验丰富前端开发工程师。我要求你帮我开发一个钉钉机器人。 有以下要求: 1. 需要使用got请求数据,使用typescript开发,每个接口都需要根据我给的demo定义完整的type 2. 机器人请求的URL是固定的,所以我期望在实例化的时候,传递一次url就好了。 3. 机器人一共支持4中格式。 4. 第一个格式是text,对应的方式名text,需要提交的json demo是:{"at":{"atMobiles":["180xxxxxx"],"atUserIds":["user123"],"isAtAll":false},"text":{"content":"我就是我, @XXX 是不一样的烟火"},"msgtype":"text"} 5. 第二个格式是link,对应的方法名link,需要提交的json demo是:{"msgtype":"link","link":{"text":"这个即将发布的新","title":"时代的火车向前开","picUrl":"","messageUrl":"https://www.dingtalk.com"}} 6. 第三个格式是markdown,方法名是markdown,demo json 是:{"msgtype":"markdown","markdown":{"title":"杭州天气","text":"#### 杭州天气 @150XXXXXXXX \n > 9度"},"at":{"atMobiles":["150XXXXXXXX"],"atUserIds":["user123"],"isAtAll":false}} 7. 第四个是actionCard,方法名是actionCard,demo 数据是:{"actionCard":{"title":"乔布斯 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身","text":"### 乔布斯 20 年前想打","btnOrientation":"0","singleTitle":"阅读全文","singleURL":"https://www.dingtalk.com/"},"msgtype":"actionCard"} 8. 第五个是feedCard类型,方法名是feedCard,demo数据是:{"msgtype":"feedCard","feedCard":{"links":[{"title":"时代的火车向前开1","messageURL":"https://www.dingtalk.com/","picURL":"https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png"},{"title":"时代的火车向前开2","messageURL":"https://www.dingtalk.com/","picURL":"https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png"}]}}1 / 1
首先,我们需要安装got
库,用于处理HTTP请求,以及typescript
。在项目根目录下执行以下命令:
bashCopy codenpm install got npm install typescript
然后,我们创建一个新的TypeScript文件,如dingtalk-bot.ts
。首先导入所需的库和定义一些类型:
typescriptCopy codeimport got from "got";
type AtInfo = {
atMobiles: string[];
atUserIds: string[];
isAtAll: boolean;
};
type TextMessage = {
at: AtInfo;
text: { content: string };
msgtype: "text";
};
type LinkMessage = {
msgtype: "link";
link: {
text: string;
title: string;
picUrl: string;
messageUrl: string;
};
};
type MarkdownMessage = {
msgtype: "markdown";
markdown: { title: string; text: string };
at: AtInfo;
};
type ActionCardMessage = {
actionCard: {
title: string;
text: string;
btnOrientation: string;
singleTitle: string;
singleURL: string;
};
msgtype: "actionCard";
};
type FeedCardLink = {
title: string;
messageURL: string;
picURL: string;
};
type FeedCardMessage = {
msgtype: "feedCard";
feedCard: { links: FeedCardLink[] };
};
type DingtalkMessage = TextMessage | LinkMessage | MarkdownMessage | ActionCardMessage | FeedCardMessage;
接下来,我们创建一个DingtalkBot
类并实现相关方法:
typescriptCopy codeclass DingtalkBot {
private apiUrl: string;
constructor(apiUrl: string) {
this.apiUrl = apiUrl;
}
async sendMessage(message: DingtalkMessage): Promise<void> {
await got.post(this.apiUrl, {
json: message,
responseType: "json",
});
}
async text(at: AtInfo, content: string): Promise<void> {
const message: TextMessage = {
at,
text: { content },
msgtype: "text",
};
await this.sendMessage(message);
}
async link(linkInfo: LinkMessage["link"]): Promise<void> {
const message: LinkMessage = {
link: linkInfo,
msgtype: "link",
};
await this.sendMessage(message);
}
async markdown(at: AtInfo, title: string, text: string): Promise<void> {
const message: MarkdownMessage = {
at,
markdown: { title, text },
msgtype: "markdown",
};
await this.sendMessage(message);
}
async actionCard(actionCardInfo: ActionCardMessage["actionCard"]): Promise<void> {
const message: ActionCardMessage = {
actionCard: actionCardInfo,
msgtype: "actionCard",
};
await this.sendMessage(message);
}
async feedCard(links: FeedCardLink[]): Promise<void> {
const message: FeedCardMessage = {
feedCard: { links },
msgtype: "feedCard",
};
await this.sendMessage(message);
}
}
现在,您可以在其他TypeScript文件中使用DingtalkBot
类来创建钉钉机器人并发送不同类型的消息。例如:
typescriptCopy codeimport { DingtalkBot } from "./dingtalk-bot";
const dingtalkBot = new DingtalkBot("https://your-webhook-url");
(async () => {
await ding
上面的代码有几个地方需要修改: 1. at 这个数据是可选的 2. 请求的机器人返回的数据,需要在对应的方法中return 出来。1 / 1
谢谢您的反馈。以下是根据您的要求修改后的代码:
首先,我们需要更新AtInfo
类型以使其可选,并更新sendMessage
方法以返回响应数据。
typescriptCopy codetype AtInfo = {
atMobiles?: string[];
atUserIds?: string[];
isAtAll?: boolean;
};
// ... 其他类型定义保持不变
class DingtalkBot {
private apiUrl: string;
constructor(apiUrl: string) {
this.apiUrl = apiUrl;
}
async sendMessage(message: DingtalkMessage): Promise<any> {
const response = await got.post(this.apiUrl, {
json: message,
responseType: "json",
});
return response.body;
}
async text(at: AtInfo, content: string): Promise<any> {
const message: TextMessage = {
at,
text: { content },
msgtype: "text",
};
return await this.sendMessage(message);
}
async link(linkInfo: LinkMessage["link"]): Promise<any> {
const message: LinkMessage = {
link: linkInfo,
msgtype: "link",
};
return await this.sendMessage(message);
}
async markdown(at: AtInfo, title: string, text: string): Promise<any> {
const message: MarkdownMessage = {
at,
markdown: { title, text },
msgtype: "markdown",
};
return await this.sendMessage(message);
}
async actionCard(actionCardInfo: ActionCardMessage["actionCard"]): Promise<any> {
const message: ActionCardMessage = {
actionCard: actionCardInfo,
msgtype: "actionCard",
};
return await this.sendMessage(message);
}
async feedCard(links: FeedCardLink[]): Promise<any> {
const message: FeedCardMessage = {
feedCard: { links },
msgtype: "feedCard",
};
return await this.sendMessage(message);
}
}
现在,您可以在其他TypeScript文件中使用DingtalkBot
类来创建钉钉机器人并发送不同类型的消息,并处理返回的响应数据。例如:
typescriptCopy codeimport { DingtalkBot } from "./dingtalk-bot";
const dingtalkBot = new DingtalkBot("https://your-webhook-url");
(async () => {
const textResponse = await dingtalkBot.text({ isAtAll: false }, "这是一条文本消息");
console.log("Text response:", textResponse);
const linkResponse = await dingtalkBot.link({
text: "这个即将发布的新",
title: "时代的火车向前开",
picUrl: "",
messageUrl: "https://www.dingtalk.com",
});
console.log("Link response:", linkResponse);
// ... 其他类型的消息及响应处理
})();
请帮我根据上面的代码,写一份readme1 / 1
钉钉机器人 TypeScript SDK
这是一个使用 TypeScript 编写的钉钉机器人SDK,用于发送不同类型的消息到钉钉群聊。
安装
首先,确保你已经安装了Node.js
和npm
。然后,在你的项目根目录下执行以下命令来安装所需的依赖:
bashCopy codenpm install got npm install typescript
用法
- 将上面提供的
dingtalk-bot.ts
文件添加到您的项目中。 - 在您需要使用钉钉机器人的地方,导入
DingtalkBot
类并创建一个实例。
下面是一个简单的例子:
typescriptCopy codeimport { DingtalkBot } from "./dingtalk-bot";
const dingtalkBot = new DingtalkBot("https://your-webhook-url");
(async () => {
// 发送文本消息
const textResponse = await dingtalkBot.text({ isAtAll: false }, "这是一条文本消息");
console.log("Text response:", textResponse);
// 发送链接消息
const linkResponse = await dingtalkBot.link({
text: "这个即将发布的新",
title: "时代的火车向前开",
picUrl: "",
messageUrl: "https://www.dingtalk.com",
});
console.log("Link response:", linkResponse);
// 发送Markdown消息
const markdownResponse = await dingtalkBot.markdown({ isAtAll: false }, "杭州天气", "#### 杭州天气\n > 9度");
console.log("Markdown response:", markdownResponse);
// 发送ActionCard消息
const actionCardResponse = await dingtalkBot.actionCard({
title: "乔布斯 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身",
text: "### 乔布斯 20 年前想打",
btnOrientation: "0",
singleTitle: "阅读全文",
singleURL: "https://www.dingtalk.com/",
});
console.log("ActionCard response:", actionCardResponse);
// 发送FeedCard消息
const feedCardResponse = await dingtalkBot.feedCard([
{
title: "时代的火车向前开1",
messageURL: "https://www.dingtalk.com/",
picURL: "https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png",
},
{
title: "时代的火车向前开2",
messageURL: "https://www.dingtalk.com/",
picURL: "https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png",
},
]);
console.log("FeedCard response:", feedCardResponse);
})();
在这个例子中,我们导入了DingtalkBot
类,并使用您的钉钉机器人Webhook URL创建了一个实例。然后,我们使用该实例发送不同类型的消息,并处理返回的响应数据。
使用jest 测试框架,请帮我基于上面的代码写一下单元测试1 / 1
首先,我们需要安装jest
以及相应的TypeScript支持库。在项目根目录下执行以下命令:
bashCopy codenpm install jest ts-jest @types/jest
接下来,创建一个jest.config.js
文件,在项目根目录下:
javascriptCopy codemodule.exports = {
preset: "ts-jest",
testEnvironment: "node",
testPathIgnorePatterns: ["/node_modules/"],
};
现在,我们创建一个新的TypeScript文件,例如dingtalk-bot.test.ts
,并编写针对DingtalkBot
类的单元测试:
typescriptCopy codeimport { DingtalkBot } from "./dingtalk-bot";
import got from "got";
jest.mock("got");
const mockedGot = got as jest.MockedFunction<typeof got>;
describe("DingtalkBot", () => {
const apiUrl = "https://your-webhook-url";
let dingtalkBot: DingtalkBot;
beforeEach(() => {
dingtalkBot = new DingtalkBot(apiUrl);
});
afterEach(() => {
jest.clearAllMocks();
});
it("should send text messages", async () => {
mockedGot.post.mockResolvedValue({ body: "success" });
const response = await dingtalkBot.text({ isAtAll: false }, "这是一条文本消息");
expect(response).toBe("success");
expect(mockedGot.post).toHaveBeenCalledWith(apiUrl, expect.objectContaining({ json: expect.any(Object) }));
});
it("should send link messages", async () => {
mockedGot.post.mockResolvedValue({ body: "success" });
const response = await dingtalkBot.link({
text: "这个即将发布的新",
title: "时代的火车向前开",
picUrl: "",
messageUrl: "https://www.dingtalk.com",
});
expect(response).toBe("success");
expect(mockedGot.post).toHaveBeenCalledWith(apiUrl, expect.objectContaining({ json: expect.any(Object) }));
});
// 类似地,为其他消息类型编写测试用例
// it("should send markdown messages", async () => { ... });
// it("should send actionCard messages", async () => { ... });
// it("should send feedCard messages", async () => { ... });
});
在这个测试文件中,我们首先导入了DingtalkBot
类和got
库。然后,我们使用jest.mock
模拟了got
库的post
方法,以便在测试中捕获传递给它的参数。
我们为DingtalkBot
类创建了一个测试套件,并在其中定义了几个测试用例,分别针对不同类型的消息。在每个测试用例中,我们调用DingtalkBot
的相应方法,并使用expect
断言来检查got.post
是否被正确调用。
要运行这些测试,只需在项目根目录下执行npx jest
命令。Jest将自动检测并运行您的测试用例。
上面的DingtalkBot class中, 1. 需要给每一个方法增加类型注释,来说明参数的用途。 2. 方法中如果有at参数,放到最后一个参数上,然后设置为可选类型1 / 1
根据您的要求,我对DingtalkBot
类进行了修改,如下所示:
typescriptCopy codeclass DingtalkBot {
private apiUrl: string;
constructor(apiUrl: string) {
this.apiUrl = apiUrl;
}
async sendMessage(message: DingtalkMessage): Promise<any> {
const response = await got.post(this.apiUrl, {
json: message,
responseType: "json",
});
return response.body;
}
/**
* 发送文本消息
* @param content 文本消息内容
* @param at 可选,@相关信息
*/
async text(content: string, at?: AtInfo): Promise<any> {
const message: TextMessage = {
at,
text: { content },
msgtype: "text",
};
return await this.sendMessage(message);
}
/**
* 发送链接消息
* @param linkInfo 链接消息内容
*/
async link(linkInfo: LinkMessage["link"]): Promise<any> {
const message: LinkMessage = {
link: linkInfo,
msgtype: "link",
};
return await this.sendMessage(message);
}
/**
* 发送Markdown消息
* @param title Markdown消息的标题
* @param text Markdown格式的消息内容
* @param at 可选,@相关信息
*/
async markdown(title: string, text: string, at?: AtInfo): Promise<any> {
const message: MarkdownMessage = {
at,
markdown: { title, text },
msgtype: "markdown",
};
return await this.sendMessage(message);
}
/**
* 发送ActionCard消息
* @param actionCardInfo ActionCard消息内容
*/
async actionCard(actionCardInfo: ActionCardMessage["actionCard"]): Promise<any> {
const message: ActionCardMessage = {
actionCard: actionCardInfo,
msgtype: "actionCard",
};
return await this.sendMessage(message);
}
/**
* 发送FeedCard消息
* @param links FeedCard消息中的链接列表
*/
async feedCard(links: FeedCardLink[]): Promise<any> {
const message: FeedCardMessage = {
feedCard: { links },
msgtype: "feedCard",
};
return await this.sendMessage(message);
}
}
在上面的代码中,我为每个方法添加了类型注释,以说明参数的用途。同时,我将at
参数移动到了text
和markdown
方法的最后一个参数,并将其设置为可选类型。这样,在调用这两个方法时,如果不需要at
信息,可以省略该参数。
上面的单测中有这个报错:Property 'mockResolvedValue' does not exist on type 'GotRequestFunction'. (tsserver 2339)1 / 1
抱歉给您带来了困扰。在 Jest 中,当我们使用 jest.mock()
之后,我们需要在测试用例中指定函数的行为,这里我们应该使用 mockResolvedValueOnce
方法。
以下是更新后的单元测试代码:
typescriptCopy codeimport { DingtalkBot } from "./dingtalk-bot";
import got from "got";
jest.mock("got");
const mockedGot = got as jest.MockedFunction<typeof got>;
describe("DingtalkBot", () => {
const apiUrl = "https://your-webhook-url";
let dingtalkBot: DingtalkBot;
beforeEach(() => {
dingtalkBot = new DingtalkBot(apiUrl);
});
afterEach(() => {
jest.clearAllMocks();
});
it("should send text messages", async () => {
mockedGot.post.mockResolvedValueOnce({ body: "success" });
const response = await dingtalkBot.text("这是一条文本消息");
expect(response).toBe("success");
expect(mockedGot.post).toHaveBeenCalledWith(apiUrl, expect.objectContaining({ json: expect.any(Object) }));
});
it("should send link messages", async () => {
mockedGot.post.mockResolvedValueOnce({ body: "success" });
const response = await dingtalkBot.link({
text: "这个即将发布的新",
title: "时代的火车向前开",
picUrl: "",
messageUrl: "https://www.dingtalk.com",
});
expect(response).toBe("success");
expect(mockedGot.post).toHaveBeenCalledWith(apiUrl, expect.objectContaining({ json: expect.any(Object) }));
});
// 类似地,为其他消息类型编写测试用例
// it("should send markdown messages", async () => { ... });
// it("should send actionCard messages", async () => { ... });
// it("should send feedCard messages", async () => { ... });
});
在此修改中,我将 mockResolvedValue
替换为 mockResolvedValueOnce
。现在,您应该可以运行测试而不会遇到之前的错误。
mockResolvedValueOnce这个方法也不存在,请你重新确认一下jest的函数mock用法1 / 1
非常抱歉,我之前的回答有误。在这种情况下,您需要使用mockImplementation
来模拟got.post
的实现。以下是修复后的单元测试代码:
typescriptCopy codeimport { DingtalkBot } from "./dingtalk-bot";
import got from "got";
jest.mock("got");
const mockedGot = got as jest.MockedFunction<typeof got>;
describe("DingtalkBot", () => {
const apiUrl = "https://your-webhook-url";
let dingtalkBot: DingtalkBot;
beforeEach(() => {
dingtalkBot = new DingtalkBot(apiUrl);
});
afterEach(() => {
jest.clearAllMocks();
});
it("should send text messages", async () => {
mockedGot.post.mockImplementation(async () => {
return { body: "success" };
});
const response = await dingtalkBot.text("这是一条文本消息");
expect(response).toBe("success");
expect(mockedGot.post).toHaveBeenCalledWith(apiUrl, expect.objectContaining({ json: expect.any(Object) }));
});
it("should send link messages", async () => {
mockedGot.post.mockImplementation(async () => {
return { body: "success" };
});
const response = await dingtalkBot.link({
text: "这个即将发布的新",
title: "时代的火车向前开",
picUrl: "",
messageUrl: "https://www.dingtalk.com",
});
expect(response).toBe("success");
expect(mockedGot.post).toHaveBeenCalledWith(apiUrl, expect.objectContaining({ json: expect.any(Object) }));
});
// 类似地,为其他消息类型编写测试用例
// it("should send markdown messages", async () => { ... });
// it("should send actionCard messages", async () => { ... });
// it("should send feedCard messages", async () => { ... });
});
在这里,我使用mockImplementation
方法模拟了got.post
函数的实现,并返回一个包含body
属性的对象。这样,当调用got.post
时,它将返回我们指定的模拟实现,而不是实际实现。现在,您应该可以运行测试而不会遇到之前的错误。
还是不对,你是不是应该需要mock 一下got的post 方法1 / 1
非常抱歉给您带来困扰。您是对的,我们需要单独模拟got.post
方法。以下是修复后的单元测试代码:
typescriptCopy codeimport { DingtalkBot } from "./dingtalk-bot";
import got from "got";
const { post } = got;
jest.mock("got");
const mockedGot = got as jest.Mocked<typeof got>;
const mockedPost = post as jest.MockedFunction<typeof post>;
describe("DingtalkBot", () => {
const apiUrl = "https://your-webhook-url";
let dingtalkBot: DingtalkBot;
beforeEach(() => {
dingtalkBot = new DingtalkBot(apiUrl);
});
afterEach(() => {
jest.clearAllMocks();
});
it("should send text messages", async () => {
mockedPost.mockResolvedValueOnce({ body: "success" } as any);
const response = await dingtalkBot.text("这是一条文本消息");
expect(response).toBe("success");
expect(mockedPost).toHaveBeenCalledWith(apiUrl, expect.objectContaining({ json: expect.any(Object) }));
});
it("should send link messages", async () => {
mockedPost.mockResolvedValueOnce({ body: "success" } as any);
const response = await dingtalkBot.link({
text: "这个即将发布的新",
title: "时代的火车向前开",
picUrl: "",
messageUrl: "https://www.dingtalk.com",
});
expect(response).toBe("success");
expect(mockedPost).toHaveBeenCalledWith(apiUrl, expect.objectContaining({ json: expect.any(Object) }));
});
// 类似地,为其他消息类型编写测试用例
// it("should send markdown messages", async () => { ... });
// it("should send actionCard messages", async () => { ... });
// it("should send feedCard messages", async () => { ... });
});
在这里,我们先解构出got
中的post
方法。然后,我们使用jest.mock()
模拟整个got
库。我们分别创建mockedGot
和mockedPost
变量来存储模拟后的got
和post
方法。接下来,我们使用mockResolvedValueOnce
方法为mockedPost
设置返回值。现在,您应该可以运行测试而不会遇到之前的错误。
还是不对,你是不是应该需要mock 一下got的post 方法 1 / 1
非常抱歉,我之前的回答没有解决问题。请您再次尝试以下的解决方案。这次,我将使用更简洁的方法来单独模拟 got.post
方法。
typescriptCopy codeimport { DingtalkBot } from "./dingtalk-bot";
import got from "got";
jest.mock("got", () => {
return {
post: jest.fn(),
};
});
const mockedPost = got.post as jest.MockedFunction<typeof got.post>;
describe("DingtalkBot", () => {
const apiUrl = "https://your-webhook-url";
let dingtalkBot: DingtalkBot;
beforeEach(() => {
dingtalkBot = new DingtalkBot(apiUrl);
});
afterEach(() => {
jest.clearAllMocks();
});
it("should send text messages", async () => {
mockedPost.mockResolvedValueOnce({ body: "success" } as any);
const response = await dingtalkBot.text("这是一条文本消息");
expect(response).toBe("success");
expect(mockedPost).toHaveBeenCalledWith(apiUrl, expect.objectContaining({ json: expect.any(Object) }));
});
it("should send link messages", async () => {
mockedPost.mockResolvedValueOnce({ body: "success" } as any);
const response = await dingtalkBot.link({
text: "这个即将发布的新",
title: "时代的火车向前开",
picUrl: "",
messageUrl: "https://www.dingtalk.com",
});
expect(response).toBe("success");
expect(mockedPost).toHaveBeenCalledWith(apiUrl, expect.objectContaining({ json: expect.any(Object) }));
});
// 类似地,为其他消息类型编写测试用例
// it("should send markdown messages", async () => { ... });
// it("should send actionCard messages", async () => { ... });
// it("should send feedCard messages", async () => { ... });
});
在这里,我们直接在 jest.mock()
中模拟 got
库,并仅模拟 post
方法。然后,我们创建 mockedPost
变量来存储模拟后的 post
方法。接下来,我们使用 mockResolvedValueOnce
方法为 mockedPost
设置返回值。现在,您应该可以运行测试而不会遇到之前的错误。