feat: 喵语翻译与消息解释器

pull/13/head
moonrailgun 4 years ago
parent 6490901b64
commit 12af2a86b2

@ -0,0 +1,43 @@
## 如何创建一个插件
### 内部插件
> 内部插件是指随 `tailchat` 分发而提供的插件
在web目录执行:
```bash
yarn ministar createPlugin
```
插件名请准守反域名模式, 如: `com.msgbyte.xxx`
设置`tsconfig.json`如下:
```json
{
"compilerOptions": {
"rootDir": "./src",
"baseUrl": "./src",
"esModuleInterop": true,
"jsx": "react",
"paths": {
"@capital/*": ["../../../src/plugin/*"],
}
}
}
```
创建一个`manifest.json`文件
示例:
```json
{
"label": "网页面板插件",
"name": "com.msgbyte.webview",
"url": "/plugins/com.msgbyte.webview/index.js",
"version": "0.0.0",
"author": "msgbyte",
"description": "为群组提供创建网页面板的功能",
"requireRestart": false
}
```

@ -0,0 +1,9 @@
{
"label": "喵语言",
"name": "com.msgbyte.miaolang",
"url": "/plugins/com.msgbyte.miaolang/index.js",
"version": "0.0.0",
"author": "msgbyte",
"description": "为聊天提供喵语言对话功能",
"requireRestart": true
}

@ -0,0 +1,9 @@
{
"name": "@plugins/com.msgbyte.miaolang",
"main": "src/index.ts",
"version": "0.0.0",
"private": true,
"dependencies": {
"miao-lang": "^1.0.3"
}
}

@ -0,0 +1,19 @@
import { decode, encode, isMiao } from './trans';
import { regMessageInterpreter } from '@capital/common';
const miao = encode('喵语翻译已加载');
const human = decode(miao);
console.log(`${miao}\n${human}`);
regMessageInterpreter({
name: '喵语翻译',
explainMessage(message: string) {
// 喵语 -> 人话
if (!isMiao(message)) {
return null;
}
return decode(message);
},
});

@ -0,0 +1,13 @@
import Miao from 'miao-lang';
export function encode(human: string): string {
return Miao.encode(human);
}
export function decode(miao: string): string {
return Miao.decode(miao);
}
export function isMiao(input: string): boolean {
return Miao.isMiao(input);
}

@ -0,0 +1,11 @@
{
"compilerOptions": {
"rootDir": "./src",
"baseUrl": "./src",
"esModuleInterop": true,
"jsx": "react",
"paths": {
"@capital/*": ["../../../src/plugin/*"],
}
}
}

@ -0,0 +1,15 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
js-base64@^3.6.0:
version "3.6.1"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.6.1.tgz#555aae398b74694b4037af1f8a5a6209d170efbe"
integrity sha512-Frdq2+tRRGLQUIQOgsIGSCd1VePCS2fsddTG5dTCqR0JHgltXWfsxnY0gIXPoMeRmdom6Oyq+UMOFg5suduOjQ==
miao-lang@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/miao-lang/-/miao-lang-1.0.3.tgz#276999788f6eee2b600db66a0d72b3f53bcbd6ab"
integrity sha512-AXxAKfRUSFwvF0RMJ8qkUtUbVHdNqGA71k+4/38ikh2mK1iRoiDueuTannVF+9NqzSXi770GKMuGEFXu/+Ithg==
dependencies:
js-base64 "^3.6.0"

@ -0,0 +1,9 @@
{
"label": "网页面板插件",
"name": "com.msgbyte.webview",
"url": "/plugins/com.msgbyte.webview/index.js",
"version": "0.0.0",
"author": "msgbyte",
"description": "为群组提供创建网页面板的功能",
"requireRestart": false
}

@ -6,6 +6,7 @@ import {
} from 'tailchat-shared';
import { Avatar } from '@/components/Avatar';
import clsx from 'clsx';
import { useRenderPluginMessageInterpreter } from './useRenderPluginMessageInterpreter';
interface ChatMessageItemProps {
showAvatar: boolean;
@ -29,7 +30,7 @@ export const ChatMessageItem: React.FC<ChatMessageItemProps> = React.memo(
</div>
)}
</div>
<div className="flex flex-col flex-1 overflow-auto">
<div className="flex flex-col flex-1 overflow-auto group">
{showAvatar && (
<div className="flex items-center">
<div className="font-bold">{userInfo.nickname}</div>
@ -39,7 +40,12 @@ export const ChatMessageItem: React.FC<ChatMessageItemProps> = React.memo(
</div>
)}
<div className="leading-6 break-words">{payload.content}</div>
<div className="leading-6 break-words">
<span>{payload.content}</span>
{/* 解释器按钮 */}
{useRenderPluginMessageInterpreter(payload.content)}
</div>
</div>
</div>
);

@ -0,0 +1,52 @@
import { messageInterpreter } from '@/plugin/common';
import { Icon } from '@iconify/react';
import { Popover } from 'antd';
import React from 'react';
import { useMemo } from 'react';
import { t } from 'tailchat-shared';
export function useRenderPluginMessageInterpreter(message: string) {
const availableInterpreter = useMemo(
() =>
messageInterpreter
.map(({ name, explainMessage }) => ({
name,
render: explainMessage(message),
}))
.filter(({ render }) => render !== null),
[message]
);
if (availableInterpreter.length === 0) {
return null;
}
return (
<span className="align-middle hidden group-hover:inline-block">
<Popover
placement="topLeft"
title={t('消息解释')}
content={
<div className="max-w-lg">
{availableInterpreter.map((ai, i) => (
<p key={i + (ai.name ?? '')}>
{ai.name && (
<span>
{t('来自')} {ai.name} :{' '}
</span>
)}
{ai.render}
</p>
))}
</div>
}
trigger="click"
>
<Icon
className="cursor-pointer text-base"
icon="mdi:file-question-outline"
/>
</Popover>
</span>
);
}

@ -32,3 +32,15 @@ export interface PluginGroupPanel {
}
export const [pluginGroupPanel, regGroupPanel] =
buildRegList<PluginGroupPanel>();
export interface PluginMessageInterpreter {
name?: string;
explainMessage: (message: string) => React.ReactElement | null;
}
/**
*
* ,
*/
export const [messageInterpreter, regMessageInterpreter] =
buildRegList<PluginMessageInterpreter>();

Loading…
Cancel
Save