Commit 4052affc authored by nanahira's avatar nanahira

support consumer message

parent 0e892e04
...@@ -101,4 +101,11 @@ app.command('survey', '卡片调查').action(async ({ session }) => { ...@@ -101,4 +101,11 @@ app.command('survey', '卡片调查').action(async ({ session }) => {
} }
}); });
app.command('link').action(() => '<a href="https://koishi.chat">Koishi</a>');
app.command('wait <time:posint>').action(async (argv, time) => {
await new Promise((resolve) => setTimeout(resolve, time * 1000));
return `等待了 ${time} 秒`;
});
app.start(); app.start();
This diff is collapsed.
...@@ -49,11 +49,11 @@ ...@@ -49,11 +49,11 @@
"form-data": "^4.0.0", "form-data": "^4.0.0",
"koa-wechat-public": "^0.1.13", "koa-wechat-public": "^0.1.13",
"koa-xml-body": "^2.2.0", "koa-xml-body": "^2.2.0",
"koishi-thirdeye": "^11.1.21", "koishi-thirdeye": "^11.1.24",
"mime2ext": "^1.0.1" "mime2ext": "^1.0.1"
}, },
"peerDependencies": { "peerDependencies": {
"koishi": "^4.11.1" "koishi": "^4.11.6"
}, },
"devDependencies": { "devDependencies": {
"@koishijs/plugin-help": "^2.0.2", "@koishijs/plugin-help": "^2.0.2",
......
...@@ -34,6 +34,7 @@ import path from 'path'; ...@@ -34,6 +34,7 @@ import path from 'path';
import { Readable } from 'stream'; import { Readable } from 'stream';
import { WechatAdapter } from './adapter'; import { WechatAdapter } from './adapter';
import XmlParser from 'koa-xml-body'; import XmlParser from 'koa-xml-body';
import { WechatConsumerMessenger } from './message';
export * from './config'; export * from './config';
declare module 'koishi' { declare module 'koishi' {
...@@ -54,7 +55,7 @@ export default class WechatBot extends Bot<Partial<WechatOfficialConfig>> { ...@@ -54,7 +55,7 @@ export default class WechatBot extends Bot<Partial<WechatOfficialConfig>> {
internal: WechatApplication; internal: WechatApplication;
private menuMap = new Map<string, string>(); private menuMap = new Map<string, string>();
@Inject() @Inject()
private http: Quester; http: Quester;
@InjectLogger() @InjectLogger()
private logger: Logger; private logger: Logger;
...@@ -130,7 +131,7 @@ export default class WechatBot extends Bot<Partial<WechatOfficialConfig>> { ...@@ -130,7 +131,7 @@ export default class WechatBot extends Bot<Partial<WechatOfficialConfig>> {
} }
} }
private async uploadMedia(element: Element) { async uploadMedia(element: Element) {
try { try {
const { type } = element; const { type } = element;
const uploadType = type === 'audio' ? 'voice' : type; const uploadType = type === 'audio' ? 'voice' : type;
...@@ -213,7 +214,7 @@ export default class WechatBot extends Bot<Partial<WechatOfficialConfig>> { ...@@ -213,7 +214,7 @@ export default class WechatBot extends Bot<Partial<WechatOfficialConfig>> {
session.subtype = 'private'; session.subtype = 'private';
session.author = { userId: acc.fromUser }; session.author = { userId: acc.fromUser };
} }
const reply = await session.process(); const reply = await session.process(4500);
if (reply?.length) { if (reply?.length) {
await this.setReply(acc.send, segment.normalize(reply)); await this.setReply(acc.send, segment.normalize(reply));
} }
...@@ -280,14 +281,16 @@ export default class WechatBot extends Bot<Partial<WechatOfficialConfig>> { ...@@ -280,14 +281,16 @@ export default class WechatBot extends Bot<Partial<WechatOfficialConfig>> {
guildId?: string, guildId?: string,
options?: SendOptions, options?: SendOptions,
): Promise<string[]> { ): Promise<string[]> {
return []; return this.sendPrivateMessage(channelId, content, options);
} }
async sendPrivateMessage( async sendPrivateMessage(
userId: string, userId: string,
content: segment.Fragment, content: segment.Fragment,
options?: SendOptions, options?: SendOptions,
): Promise<string[]> { ): Promise<string[]> {
return []; return new WechatConsumerMessenger(this, userId, undefined, options).send(
content,
);
} }
async getMessage( async getMessage(
channelId: string, channelId: string,
......
import { Logger, Messenger, Random, segment } from 'koishi';
import WechatBot from './index';
interface ConsumerMessagePayload {
msgtype: 'text' | 'image' | 'voice' | 'video';
touser?: string;
text?: {
content: string;
};
image?: {
media_id: string;
};
voice?: {
media_id: string;
};
video?: {
media_id: string;
thumb_media_id?: string;
title: string;
description: string;
};
}
export class WechatConsumerMessenger extends Messenger<WechatBot> {
logger = new Logger('wechat-consumer-messenger');
async sendConsumer(payload: ConsumerMessagePayload) {
const token = await this.bot.internal.getAccessToken();
let success = false;
try {
const result = await this.bot.http.post<{
errcode: number;
errmsg: string;
}>(
'https://api.weixin.qq.com/cgi-bin/message/custom/send',
{
touser: this.channelId,
...payload,
},
{
params: {
access_token: token,
},
},
);
if (result.errcode > 0) {
this.logger.warn(
`Send consumer message failed: ${result.errcode} ${result.errmsg}`,
);
} else {
success = true;
}
} catch (e) {
this.logger.error(`Send consumer message errored: ${e.message}`);
}
if (success) {
const session = this.bot.session();
session.messageId = Random.id();
session.app.emit(session, 'send', session);
this.results.push(session);
}
}
private buffer = '';
async flush() {
const content = this.buffer.trim();
if (content) {
await this.sendConsumer({
msgtype: 'text',
text: {
content,
},
});
}
}
text(text: string) {
this.buffer += text;
}
async visit(element: segment) {
const { type, attrs, children } = element;
switch (type) {
case 'text':
this.text(attrs.content);
break;
case 'p':
await this.render(children);
this.text('\n');
break;
case 'a':
this.text(element.toString());
break;
case 'at':
if (attrs.id) {
this.text(`@${attrs.id}`);
} else if (attrs.type === 'all') {
this.text('@全体成员');
} else if (attrs.type === 'here') {
this.text('@在线成员');
} else if (attrs.role) {
this.text(`@${attrs.role}`);
}
break;
case 'sharp':
this.text(` #${attrs.name} `);
break;
case 'message':
await this.flush();
await this.render(children);
await this.flush();
break;
case 'image':
case 'voice':
case 'video':
await this.flush();
const mediaId = await this.bot.uploadMedia(element);
if (mediaId) {
await this.sendConsumer({
msgtype: type,
[type]: {
media_id: mediaId,
title: attrs.title,
description: attrs.description,
},
});
}
break;
default:
await this.render(children);
}
}
}
import { Element } from 'koishi'; import { Element } from 'koishi';
const patternTypes = ['message', 'p', 'a']; const patternTypes = ['message', 'p'];
export function getFirstElement(elements: Element[]): Element { export function getFirstElement(elements: Element[]): Element {
if (!elements.length) return; if (!elements.length) return;
...@@ -18,12 +18,11 @@ export function getPlainText(elements: Element[]) { ...@@ -18,12 +18,11 @@ export function getPlainText(elements: Element[]) {
for (const element of elements) { for (const element of elements) {
if (element.type === 'text') { if (element.type === 'text') {
buffer += element.attrs.content; buffer += element.attrs.content;
} else if (element.type === 'a') {
buffer += element.toString();
} else if (patternTypes.includes(element.type)) { } else if (patternTypes.includes(element.type)) {
buffer += getPlainText(element.children); buffer += getPlainText(element.children);
buffer += '\n'; buffer += '\n';
if (element.type === 'a' && element.attrs.href) {
buffer += ` (${element.attrs.href})`;
}
} else if (element.type === 'at') { } else if (element.type === 'at') {
if (element.attrs.id) { if (element.attrs.id) {
buffer += `@${element.attrs.id}`; buffer += `@${element.attrs.id}`;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment