mirror of https://github.com/msgbyte/tailchat
				
				
				
			feat: add welcome plugin for user which can send welcome message when user join group
welcome text support rich syntax and spec tagpull/90/head
							parent
							
								
									1c8108b36b
								
							
						
					
					
						commit
						dbebbc54e6
					
				@ -0,0 +1,14 @@
 | 
			
		||||
const path = require('path');
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  externalDeps: [
 | 
			
		||||
    'react',
 | 
			
		||||
    'react-router',
 | 
			
		||||
    'axios',
 | 
			
		||||
    'styled-components',
 | 
			
		||||
    'zustand',
 | 
			
		||||
    'zustand/middleware/immer',
 | 
			
		||||
  ],
 | 
			
		||||
  pluginRoot: path.resolve(__dirname, './web'),
 | 
			
		||||
  outDir: path.resolve(__dirname, '../../public'),
 | 
			
		||||
};
 | 
			
		||||
@ -0,0 +1,20 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "tailchat-plugin-welcome",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "main": "index.js",
 | 
			
		||||
  "author": "moonrailgun",
 | 
			
		||||
  "description": "加入群组时发送欢迎消息",
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "build:web": "ministar buildPlugin all",
 | 
			
		||||
    "build:web:watch": "ministar watchPlugin all"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@types/react": "18.0.20",
 | 
			
		||||
    "mini-star": "*"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "tailchat-server-sdk": "*"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,95 @@
 | 
			
		||||
import { call, TcContext } from 'tailchat-server-sdk';
 | 
			
		||||
import { TcService } from 'tailchat-server-sdk';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 入群欢迎
 | 
			
		||||
 *
 | 
			
		||||
 * 加入群组时发送欢迎消息
 | 
			
		||||
 */
 | 
			
		||||
class WelcomeService extends TcService {
 | 
			
		||||
  get serviceName() {
 | 
			
		||||
    return 'plugin:com.msgbyte.welcome';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onInit() {
 | 
			
		||||
    this.registryAfterActionHook('group.joinGroup', 'joinGroupCallback'); // not work
 | 
			
		||||
 | 
			
		||||
    this.registerAction('joinGroupCallback', this.joinGroupCallback, {
 | 
			
		||||
      params: {
 | 
			
		||||
        groupId: 'string',
 | 
			
		||||
      },
 | 
			
		||||
      visibility: 'public',
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async joinGroupCallback(
 | 
			
		||||
    ctx: TcContext<{
 | 
			
		||||
      groupId: string;
 | 
			
		||||
    }>
 | 
			
		||||
  ) {
 | 
			
		||||
    const { groupId } = ctx.params;
 | 
			
		||||
 | 
			
		||||
    const groupInfo = await call(ctx).getGroupInfo(groupId);
 | 
			
		||||
 | 
			
		||||
    if (groupInfo.config['plugin:groupWelcomeText']) {
 | 
			
		||||
      // 有欢迎词
 | 
			
		||||
 | 
			
		||||
      const lobbyConverseId = await call(ctx).getGroupLobbyConverseId(groupId);
 | 
			
		||||
 | 
			
		||||
      if (!lobbyConverseId) {
 | 
			
		||||
        // 如果没有文本频道则跳过
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const [welcomeText, meta] = await parseGroupWelcomeText(
 | 
			
		||||
        ctx,
 | 
			
		||||
        groupInfo.config['plugin:groupWelcomeText']
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await ctx.call(
 | 
			
		||||
        'chat.message.sendMessage',
 | 
			
		||||
        {
 | 
			
		||||
          converseId: lobbyConverseId,
 | 
			
		||||
          groupId: groupId,
 | 
			
		||||
          content: welcomeText,
 | 
			
		||||
          meta,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          meta: {
 | 
			
		||||
            ...ctx.meta,
 | 
			
		||||
            userId: groupInfo.owner, // 以群组owner的名义
 | 
			
		||||
          },
 | 
			
		||||
        }
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default WelcomeService;
 | 
			
		||||
 | 
			
		||||
async function parseGroupWelcomeText(
 | 
			
		||||
  ctx: TcContext,
 | 
			
		||||
  welcomeText: string
 | 
			
		||||
): Promise<[string, {}]> {
 | 
			
		||||
  const meta: Record<string, any> = {};
 | 
			
		||||
  if (
 | 
			
		||||
    welcomeText.includes('{nickname}') ||
 | 
			
		||||
    welcomeText.includes('{@nickname}')
 | 
			
		||||
  ) {
 | 
			
		||||
    const memberInfo = await call(ctx).getUserInfo(ctx.meta.userId);
 | 
			
		||||
    const nickname = memberInfo.nickname;
 | 
			
		||||
    const userId = String(memberInfo._id);
 | 
			
		||||
 | 
			
		||||
    welcomeText = welcomeText.replaceAll('{nickname}', nickname);
 | 
			
		||||
 | 
			
		||||
    if (welcomeText.includes('{@nickname}')) {
 | 
			
		||||
      welcomeText = welcomeText.replaceAll(
 | 
			
		||||
        '{@nickname}',
 | 
			
		||||
        `[at=${userId}]${nickname}[/at]`
 | 
			
		||||
      );
 | 
			
		||||
      meta.mentions = [String(userId)];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return [welcomeText, meta];
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,11 @@
 | 
			
		||||
{
 | 
			
		||||
  "label": "Group Welcome",
 | 
			
		||||
  "label.zh-CN": "入群欢迎",
 | 
			
		||||
  "name": "com.msgbyte.welcome",
 | 
			
		||||
  "url": "{BACKEND}/plugins/com.msgbyte.welcome/index.js",
 | 
			
		||||
  "version": "0.0.0",
 | 
			
		||||
  "author": "moonrailgun",
 | 
			
		||||
  "description": "Send a welcome message when joining a group",
 | 
			
		||||
  "description.zh-CN": "加入群组时发送欢迎消息",
 | 
			
		||||
  "requireRestart": true
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,16 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "@plugins/com.msgbyte.welcome",
 | 
			
		||||
  "main": "src/index.tsx",
 | 
			
		||||
  "version": "0.0.0",
 | 
			
		||||
  "description": "加入群组时发送欢迎消息",
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "sync:declaration": "tailchat declaration github"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {},
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@types/styled-components": "^5.1.26",
 | 
			
		||||
    "react": "18.2.0",
 | 
			
		||||
    "styled-components": "^5.3.6"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,37 @@
 | 
			
		||||
import { regPluginGroupConfigItem } from '@capital/common';
 | 
			
		||||
import { TextArea } from '@capital/component';
 | 
			
		||||
import React, { useState } from 'react';
 | 
			
		||||
import styled from 'styled-components';
 | 
			
		||||
import { Translate } from './translate';
 | 
			
		||||
 | 
			
		||||
console.log('Plugin Group Welcome is loaded');
 | 
			
		||||
 | 
			
		||||
const Desc = styled.div`
 | 
			
		||||
  color: rgba(#999, 0.8);
 | 
			
		||||
  font-size: 9px;
 | 
			
		||||
  margin-top: 2px;
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
regPluginGroupConfigItem({
 | 
			
		||||
  name: 'groupWelcomeText',
 | 
			
		||||
  title: Translate.welcomeText,
 | 
			
		||||
  tip: Translate.welcomeTip,
 | 
			
		||||
  component: ({ value, onChange, loading }) => {
 | 
			
		||||
    const [text, setText] = useState(value ?? '');
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <>
 | 
			
		||||
        <TextArea
 | 
			
		||||
          disabled={loading}
 | 
			
		||||
          value={text}
 | 
			
		||||
          maxLength={1000}
 | 
			
		||||
          showCount={true}
 | 
			
		||||
          rows={5}
 | 
			
		||||
          onChange={(e) => setText(e.target.value)}
 | 
			
		||||
          onBlur={() => onChange(text)}
 | 
			
		||||
        />
 | 
			
		||||
        <Desc>{Translate.welcomeDesc}</Desc>
 | 
			
		||||
      </>
 | 
			
		||||
    );
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
@ -0,0 +1,15 @@
 | 
			
		||||
import { localTrans } from '@capital/common';
 | 
			
		||||
 | 
			
		||||
export const Translate = {
 | 
			
		||||
  welcomeText: localTrans({ 'zh-CN': '欢迎词', 'en-US': 'Welcome Text' }),
 | 
			
		||||
  welcomeTip: localTrans({
 | 
			
		||||
    'zh-CN': '向新成员发送入群欢迎消息',
 | 
			
		||||
    'en-US': 'Send welcome message when new member',
 | 
			
		||||
  }),
 | 
			
		||||
  welcomeDesc: localTrans({
 | 
			
		||||
    'zh-CN':
 | 
			
		||||
      '清空则视为不启用,包含部分特殊写法,如{nickname}表示用户昵称, {@nickname}表示 @ 对方。同时支持富文本语法。',
 | 
			
		||||
    'en-US':
 | 
			
		||||
      'If it is empty, it will be regarded as disabled, including some special writing, such as {nickname} means user nickname, {@nickname} means @ target member.Also supports rich text syntax.',
 | 
			
		||||
  }),
 | 
			
		||||
};
 | 
			
		||||
@ -0,0 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
  "compilerOptions": {
 | 
			
		||||
    "esModuleInterop": true,
 | 
			
		||||
    "jsx": "react",
 | 
			
		||||
    "importsNotUsedAsValues": "error"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,556 @@
 | 
			
		||||
/* eslint-disable @typescript-eslint/no-explicit-any */
 | 
			
		||||
 | 
			
		||||
/// <reference types="react" />
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 该文件由 Tailchat 自动生成
 | 
			
		||||
 * 用于插件的类型声明
 | 
			
		||||
 * 生成命令: pnpm run plugins:declaration:generate
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tailchat 通用
 | 
			
		||||
 */
 | 
			
		||||
declare module '@capital/common' {
 | 
			
		||||
  export const useGroupPanelParams: any;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 打开模态框
 | 
			
		||||
   * @deprecated 请从 @capital/component 引入
 | 
			
		||||
   */
 | 
			
		||||
  export const openModal: (
 | 
			
		||||
    content: React.ReactNode,
 | 
			
		||||
 | 
			
		||||
    props?: {
 | 
			
		||||
      /**
 | 
			
		||||
       * 是否显示右上角的关闭按钮
 | 
			
		||||
       * @default false
 | 
			
		||||
       */
 | 
			
		||||
      closable?: boolean;
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
       * 遮罩层是否可关闭
 | 
			
		||||
       */
 | 
			
		||||
      maskClosable?: boolean;
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
       * 关闭modal的回调
 | 
			
		||||
       */
 | 
			
		||||
      onCloseModal?: () => void;
 | 
			
		||||
    }
 | 
			
		||||
  ) => number;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @deprecated 请从 @capital/component 引入
 | 
			
		||||
   */
 | 
			
		||||
  export const closeModal: any;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @deprecated 请从 @capital/component 引入
 | 
			
		||||
   */
 | 
			
		||||
  export const ModalWrapper: any;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @deprecated 请从 @capital/component 引入
 | 
			
		||||
   */
 | 
			
		||||
  export const useModalContext: any;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @deprecated 请从 @capital/component 引入
 | 
			
		||||
   */
 | 
			
		||||
  export const openConfirmModal: any;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @deprecated 请从 @capital/component 引入
 | 
			
		||||
   */
 | 
			
		||||
  export const openReconfirmModal: any;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @deprecated 请从 @capital/component 引入
 | 
			
		||||
   */
 | 
			
		||||
  export const Loadable: any;
 | 
			
		||||
 | 
			
		||||
  export const getGlobalState: any;
 | 
			
		||||
 | 
			
		||||
  export const useGlobalSocketEvent: <T>(
 | 
			
		||||
    eventName: string,
 | 
			
		||||
    callback: (data: T) => void
 | 
			
		||||
  ) => void;
 | 
			
		||||
 | 
			
		||||
  export const getJWTUserInfo: () => Promise<{
 | 
			
		||||
    _id?: string;
 | 
			
		||||
    nickname?: string;
 | 
			
		||||
    email?: string;
 | 
			
		||||
    avatar?: string;
 | 
			
		||||
  }>;
 | 
			
		||||
 | 
			
		||||
  export const dataUrlToFile: any;
 | 
			
		||||
 | 
			
		||||
  export const urlSearchStringify: any;
 | 
			
		||||
 | 
			
		||||
  export const urlSearchParse: any;
 | 
			
		||||
 | 
			
		||||
  export const appendUrlSearch: any;
 | 
			
		||||
 | 
			
		||||
  export const getServiceWorkerRegistration: any;
 | 
			
		||||
 | 
			
		||||
  export const getServiceUrl: () => string;
 | 
			
		||||
 | 
			
		||||
  export const getCachedUserInfo: (
 | 
			
		||||
    userId: string,
 | 
			
		||||
    refetch?: boolean
 | 
			
		||||
  ) => Promise<{
 | 
			
		||||
    _id: string;
 | 
			
		||||
    email: string;
 | 
			
		||||
    nickname: string;
 | 
			
		||||
    discriminator: string;
 | 
			
		||||
    avatar: string | null;
 | 
			
		||||
    temporary: boolean;
 | 
			
		||||
  }>;
 | 
			
		||||
 | 
			
		||||
  export const getCachedConverseInfo: any;
 | 
			
		||||
 | 
			
		||||
  export const getCachedBaseGroupInfo: any;
 | 
			
		||||
 | 
			
		||||
  export const getCachedUserSettings: any;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 本地翻译
 | 
			
		||||
   * @example
 | 
			
		||||
   * localTrans({'zh-CN': '你好', 'en-US': 'Hello'});
 | 
			
		||||
   *
 | 
			
		||||
   * @param trans 翻译对象
 | 
			
		||||
   */
 | 
			
		||||
  export const localTrans: (trans: Record<'zh-CN' | 'en-US', string>) => string;
 | 
			
		||||
 | 
			
		||||
  export const getLanguage: any;
 | 
			
		||||
 | 
			
		||||
  export const sharedEvent: any;
 | 
			
		||||
 | 
			
		||||
  export const useAsync: <T extends (...args: any[]) => Promise<any>>(
 | 
			
		||||
    fn: T,
 | 
			
		||||
    deps?: React.DependencyList
 | 
			
		||||
  ) => { loading: boolean; value?: any; error?: Error };
 | 
			
		||||
 | 
			
		||||
  export const useAsyncFn: <T extends (...args: any[]) => Promise<any>>(
 | 
			
		||||
    fn: T,
 | 
			
		||||
    deps?: React.DependencyList
 | 
			
		||||
  ) => [{ loading: boolean; value?: any; error?: Error }, T];
 | 
			
		||||
 | 
			
		||||
  export const useAsyncRefresh: <T extends (...args: any[]) => Promise<any>>(
 | 
			
		||||
    fn: T,
 | 
			
		||||
    deps?: React.DependencyList
 | 
			
		||||
  ) => { loading: boolean; value?: any; error?: Error; refresh: () => void };
 | 
			
		||||
 | 
			
		||||
  export const useAsyncRequest: <T extends (...args: any[]) => Promise<any>>(
 | 
			
		||||
    fn: T,
 | 
			
		||||
    deps?: React.DependencyList
 | 
			
		||||
  ) => [{ loading: boolean; value?: any }, T];
 | 
			
		||||
 | 
			
		||||
  export const useEvent: any;
 | 
			
		||||
 | 
			
		||||
  export const uploadFile: any;
 | 
			
		||||
 | 
			
		||||
  export const showToasts: (
 | 
			
		||||
    message: string,
 | 
			
		||||
    type?: 'info' | 'success' | 'error' | 'warning'
 | 
			
		||||
  ) => void;
 | 
			
		||||
 | 
			
		||||
  export const showSuccessToasts: any;
 | 
			
		||||
 | 
			
		||||
  export const showErrorToasts: (error: any) => void;
 | 
			
		||||
 | 
			
		||||
  export const fetchAvailableServices: any;
 | 
			
		||||
 | 
			
		||||
  export const isValidStr: (str: any) => str is string;
 | 
			
		||||
 | 
			
		||||
  export const useGroupPanelInfo: any;
 | 
			
		||||
 | 
			
		||||
  export const sendMessage: any;
 | 
			
		||||
 | 
			
		||||
  export const showMessageTime: any;
 | 
			
		||||
 | 
			
		||||
  export const joinArray: any;
 | 
			
		||||
 | 
			
		||||
  export const useConverseMessageContext: any;
 | 
			
		||||
 | 
			
		||||
  export const navigate: any;
 | 
			
		||||
 | 
			
		||||
  export const useLocation: any;
 | 
			
		||||
 | 
			
		||||
  export const useNavigate: any;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @deprecated please use createMetaFormSchema from @capital/component
 | 
			
		||||
   */
 | 
			
		||||
  export const createFastFormSchema: any;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @deprecated please use metaFormFieldSchema from @capital/component
 | 
			
		||||
   */
 | 
			
		||||
  export const fieldSchema: any;
 | 
			
		||||
 | 
			
		||||
  export const useCurrentUserInfo: any;
 | 
			
		||||
 | 
			
		||||
  export const createPluginRequest: (pluginName: string) => {
 | 
			
		||||
    get: (actionName: string, config?: any) => Promise<any>;
 | 
			
		||||
    post: (actionName: string, data?: any, config?: any) => Promise<any>;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  export const postRequest: any;
 | 
			
		||||
 | 
			
		||||
  export const pluginCustomPanel: any;
 | 
			
		||||
 | 
			
		||||
  export const regCustomPanel: (info: {
 | 
			
		||||
    position:
 | 
			
		||||
      | 'personal'
 | 
			
		||||
      | 'setting'
 | 
			
		||||
      | 'groupdetail'
 | 
			
		||||
      | 'navbar-more'
 | 
			
		||||
      | 'navbar-group'
 | 
			
		||||
      | 'navbar-personal';
 | 
			
		||||
    icon: string;
 | 
			
		||||
    name: string;
 | 
			
		||||
    label: string;
 | 
			
		||||
    render: React.ComponentType;
 | 
			
		||||
  }) => void;
 | 
			
		||||
 | 
			
		||||
  export const pluginGroupPanel: any;
 | 
			
		||||
 | 
			
		||||
  export const regGroupPanel: any;
 | 
			
		||||
 | 
			
		||||
  export const messageInterpreter: {
 | 
			
		||||
    name?: string;
 | 
			
		||||
    explainMessage: (message: string) => React.ReactNode;
 | 
			
		||||
  }[];
 | 
			
		||||
 | 
			
		||||
  export const regMessageInterpreter: (interpreter: {
 | 
			
		||||
    name?: string;
 | 
			
		||||
    explainMessage: (message: string) => React.ReactNode;
 | 
			
		||||
  }) => void;
 | 
			
		||||
 | 
			
		||||
  export const getMessageRender: (message: string) => React.ReactNode;
 | 
			
		||||
 | 
			
		||||
  export const regMessageRender: (
 | 
			
		||||
    render: (message: string) => React.ReactNode
 | 
			
		||||
  ) => void;
 | 
			
		||||
 | 
			
		||||
  export const getMessageTextDecorators: any;
 | 
			
		||||
 | 
			
		||||
  export const regMessageTextDecorators: any;
 | 
			
		||||
 | 
			
		||||
  export const ChatInputActionContextProps: any;
 | 
			
		||||
 | 
			
		||||
  export const pluginChatInputActions: any;
 | 
			
		||||
 | 
			
		||||
  export const regChatInputAction: any;
 | 
			
		||||
 | 
			
		||||
  export const pluginChatInputButtons: any;
 | 
			
		||||
 | 
			
		||||
  export const regChatInputButton: any;
 | 
			
		||||
 | 
			
		||||
  export const regSocketEventListener: (item: {
 | 
			
		||||
    eventName: string;
 | 
			
		||||
    eventFn: (...args: any[]) => void;
 | 
			
		||||
  }) => void;
 | 
			
		||||
 | 
			
		||||
  export const pluginColorScheme: any;
 | 
			
		||||
 | 
			
		||||
  export const regPluginColorScheme: any;
 | 
			
		||||
 | 
			
		||||
  export const pluginInspectServices: any;
 | 
			
		||||
 | 
			
		||||
  export const regInspectService: any;
 | 
			
		||||
 | 
			
		||||
  export const pluginMessageExtraParsers: any;
 | 
			
		||||
 | 
			
		||||
  export const regMessageExtraParser: any;
 | 
			
		||||
 | 
			
		||||
  export const pluginRootRoute: any;
 | 
			
		||||
 | 
			
		||||
  export const regPluginRootRoute: any;
 | 
			
		||||
 | 
			
		||||
  export const pluginPanelActions: any;
 | 
			
		||||
 | 
			
		||||
  export const regPluginPanelAction: (
 | 
			
		||||
    action:
 | 
			
		||||
      | {
 | 
			
		||||
          name: string;
 | 
			
		||||
          label: string;
 | 
			
		||||
          icon: string;
 | 
			
		||||
          position: 'group';
 | 
			
		||||
          onClick: (ctx: { groupId: string; panelId: string }) => void;
 | 
			
		||||
        }
 | 
			
		||||
      | {
 | 
			
		||||
          name: string;
 | 
			
		||||
          label: string;
 | 
			
		||||
          icon: string;
 | 
			
		||||
          position: 'dm';
 | 
			
		||||
          onClick: (ctx: { converseId: string }) => void;
 | 
			
		||||
        }
 | 
			
		||||
  ) => void;
 | 
			
		||||
 | 
			
		||||
  export const pluginPermission: any;
 | 
			
		||||
 | 
			
		||||
  export const regPluginPermission: (permission: {
 | 
			
		||||
    /**
 | 
			
		||||
     * 权限唯一key, 用于写入数据库
 | 
			
		||||
     * 如果为插件则权限点应当符合命名规范, 如: plugin.com.msgbyte.github.manage
 | 
			
		||||
     */
 | 
			
		||||
    key: string;
 | 
			
		||||
    /**
 | 
			
		||||
     * 权限点显示名称
 | 
			
		||||
     */
 | 
			
		||||
    title: string;
 | 
			
		||||
    /**
 | 
			
		||||
     * 权限描述
 | 
			
		||||
     */
 | 
			
		||||
    desc: string;
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否默认开启
 | 
			
		||||
     */
 | 
			
		||||
    default: boolean;
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否依赖其他权限点
 | 
			
		||||
     */
 | 
			
		||||
    required?: string[];
 | 
			
		||||
  }) => void;
 | 
			
		||||
 | 
			
		||||
  export const pluginGroupPanelBadges: any;
 | 
			
		||||
 | 
			
		||||
  export const regGroupPanelBadge: any;
 | 
			
		||||
 | 
			
		||||
  export const pluginGroupTextPanelExtraMenus: any;
 | 
			
		||||
 | 
			
		||||
  export const regPluginGroupTextPanelExtraMenu: any;
 | 
			
		||||
 | 
			
		||||
  export const pluginUserExtraInfo: any;
 | 
			
		||||
 | 
			
		||||
  export const regUserExtraInfo: any;
 | 
			
		||||
 | 
			
		||||
  export const pluginSettings: any;
 | 
			
		||||
 | 
			
		||||
  export const regPluginSettings: any;
 | 
			
		||||
 | 
			
		||||
  export const pluginInboxItemMap: any;
 | 
			
		||||
 | 
			
		||||
  export const regPluginInboxItemMap: any;
 | 
			
		||||
 | 
			
		||||
  export const pluginGroupConfigItems: any;
 | 
			
		||||
 | 
			
		||||
  export const regPluginGroupConfigItem: any;
 | 
			
		||||
 | 
			
		||||
  export const useGroupIdContext: () => string;
 | 
			
		||||
 | 
			
		||||
  export const useGroupPanelContext: () => {
 | 
			
		||||
    groupId: string;
 | 
			
		||||
    panelId: string;
 | 
			
		||||
  } | null;
 | 
			
		||||
 | 
			
		||||
  export const useSocketContext: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tailchat 组件
 | 
			
		||||
 */
 | 
			
		||||
declare module '@capital/component' {
 | 
			
		||||
  export const Button: any;
 | 
			
		||||
 | 
			
		||||
  export const Checkbox: any;
 | 
			
		||||
 | 
			
		||||
  export const Input: any;
 | 
			
		||||
 | 
			
		||||
  export const Divider: any;
 | 
			
		||||
 | 
			
		||||
  export const Space: any;
 | 
			
		||||
 | 
			
		||||
  export const Menu: any;
 | 
			
		||||
 | 
			
		||||
  export const Table: any;
 | 
			
		||||
 | 
			
		||||
  export const Switch: any;
 | 
			
		||||
 | 
			
		||||
  export const Tooltip: any;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @link https://ant.design/components/notification-cn/
 | 
			
		||||
   */
 | 
			
		||||
  export const notification: any;
 | 
			
		||||
 | 
			
		||||
  export const Empty: React.FC<
 | 
			
		||||
    React.PropsWithChildren<{
 | 
			
		||||
      prefixCls?: string;
 | 
			
		||||
      className?: string;
 | 
			
		||||
      style?: React.CSSProperties;
 | 
			
		||||
      imageStyle?: React.CSSProperties;
 | 
			
		||||
      image?: React.ReactNode;
 | 
			
		||||
      description?: React.ReactNode;
 | 
			
		||||
    }>
 | 
			
		||||
  >;
 | 
			
		||||
 | 
			
		||||
  export const Popover: any;
 | 
			
		||||
 | 
			
		||||
  export const Tag: any;
 | 
			
		||||
 | 
			
		||||
  export const TextArea: any;
 | 
			
		||||
 | 
			
		||||
  export const Avatar: any;
 | 
			
		||||
 | 
			
		||||
  export const SensitiveText: React.FC<{ className?: string; text: string }>;
 | 
			
		||||
 | 
			
		||||
  export const Icon: React.FC<{ icon: string } & React.SVGProps<SVGSVGElement>>;
 | 
			
		||||
 | 
			
		||||
  export const CopyableText: React.FC<{
 | 
			
		||||
    className?: string;
 | 
			
		||||
    style?: React.CSSProperties;
 | 
			
		||||
    config?:
 | 
			
		||||
      | boolean
 | 
			
		||||
      | {
 | 
			
		||||
          text?: string;
 | 
			
		||||
          onCopy?: (event?: React.MouseEvent<HTMLDivElement>) => void;
 | 
			
		||||
          icon?: React.ReactNode;
 | 
			
		||||
          tooltips?: boolean | React.ReactNode;
 | 
			
		||||
          format?: 'text/plain' | 'text/html';
 | 
			
		||||
        };
 | 
			
		||||
  }>;
 | 
			
		||||
 | 
			
		||||
  export const WebFastForm: any;
 | 
			
		||||
 | 
			
		||||
  export const WebMetaForm: any;
 | 
			
		||||
 | 
			
		||||
  export const createMetaFormSchema: any;
 | 
			
		||||
 | 
			
		||||
  export const metaFormFieldSchema: any;
 | 
			
		||||
 | 
			
		||||
  export const Link: any;
 | 
			
		||||
 | 
			
		||||
  export const MessageAckContainer: any;
 | 
			
		||||
 | 
			
		||||
  export const BaseChatInputButton: any;
 | 
			
		||||
 | 
			
		||||
  export const useChatInputActionContext: any;
 | 
			
		||||
 | 
			
		||||
  export const GroupExtraDataPanel: any;
 | 
			
		||||
 | 
			
		||||
  export const Image: any;
 | 
			
		||||
 | 
			
		||||
  export const IconBtn: React.FC<{
 | 
			
		||||
    icon: string;
 | 
			
		||||
    className?: string;
 | 
			
		||||
    iconClassName?: string;
 | 
			
		||||
    size?: 'small' | 'middle' | 'large';
 | 
			
		||||
    shape?: 'circle' | 'square';
 | 
			
		||||
    title?: string;
 | 
			
		||||
    danger?: boolean;
 | 
			
		||||
    active?: boolean;
 | 
			
		||||
    disabled?: boolean;
 | 
			
		||||
    onClick?: React.MouseEventHandler<HTMLElement>;
 | 
			
		||||
  }>;
 | 
			
		||||
 | 
			
		||||
  export const PillTabs: any;
 | 
			
		||||
 | 
			
		||||
  export const PillTabPane: any;
 | 
			
		||||
 | 
			
		||||
  export const FullModalField: any;
 | 
			
		||||
 | 
			
		||||
  export const DefaultFullModalInputEditorRender: any;
 | 
			
		||||
 | 
			
		||||
  export const DefaultFullModalTextAreaEditorRender: any;
 | 
			
		||||
 | 
			
		||||
  export const openModal: (
 | 
			
		||||
    content: React.ReactNode,
 | 
			
		||||
 | 
			
		||||
    props?: {
 | 
			
		||||
      /**
 | 
			
		||||
       * 是否显示右上角的关闭按钮
 | 
			
		||||
       * @default false
 | 
			
		||||
       */
 | 
			
		||||
      closable?: boolean;
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
       * 遮罩层是否可关闭
 | 
			
		||||
       */
 | 
			
		||||
      maskClosable?: boolean;
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
       * 关闭modal的回调
 | 
			
		||||
       */
 | 
			
		||||
      onCloseModal?: () => void;
 | 
			
		||||
    }
 | 
			
		||||
  ) => number;
 | 
			
		||||
 | 
			
		||||
  export const closeModal: any;
 | 
			
		||||
 | 
			
		||||
  export const ModalWrapper: any;
 | 
			
		||||
 | 
			
		||||
  export const useModalContext: any;
 | 
			
		||||
 | 
			
		||||
  export const openConfirmModal: any;
 | 
			
		||||
 | 
			
		||||
  export const openReconfirmModal: any;
 | 
			
		||||
 | 
			
		||||
  export const Loadable: any;
 | 
			
		||||
 | 
			
		||||
  export const Loading: React.FC<{
 | 
			
		||||
    spinning: boolean;
 | 
			
		||||
    className?: string;
 | 
			
		||||
    style?: React.CSSProperties;
 | 
			
		||||
    children?: React.ReactNode;
 | 
			
		||||
  }>;
 | 
			
		||||
 | 
			
		||||
  export const LoadingSpinner: React.FC<{ tip?: string }>;
 | 
			
		||||
 | 
			
		||||
  export const LoadingOnFirst: React.FC<{
 | 
			
		||||
    spinning: boolean;
 | 
			
		||||
    className?: string;
 | 
			
		||||
    style?: React.CSSProperties;
 | 
			
		||||
    children?: React.ReactNode;
 | 
			
		||||
  }>;
 | 
			
		||||
 | 
			
		||||
  export const SidebarView: any;
 | 
			
		||||
 | 
			
		||||
  export const GroupPanelSelector: any;
 | 
			
		||||
 | 
			
		||||
  export const Emoji: any;
 | 
			
		||||
 | 
			
		||||
  export const PortalAdd: any;
 | 
			
		||||
 | 
			
		||||
  export const PortalRemove: any;
 | 
			
		||||
 | 
			
		||||
  export const ErrorBoundary: any;
 | 
			
		||||
 | 
			
		||||
  export const ErrorView: React.FC<{
 | 
			
		||||
    error: Error;
 | 
			
		||||
  }>;
 | 
			
		||||
 | 
			
		||||
  export const UserAvatar: React.FC<{
 | 
			
		||||
    userId: string;
 | 
			
		||||
    className?: string;
 | 
			
		||||
    style?: React.CSSProperties;
 | 
			
		||||
    size?: 'large' | 'small' | 'default' | number;
 | 
			
		||||
  }>;
 | 
			
		||||
 | 
			
		||||
  export const UserName: React.FC<{
 | 
			
		||||
    userId: string;
 | 
			
		||||
    className?: string;
 | 
			
		||||
    style?: React.CSSProperties;
 | 
			
		||||
  }>;
 | 
			
		||||
 | 
			
		||||
  export const Markdown: any;
 | 
			
		||||
 | 
			
		||||
  export const MarkdownEditor: any;
 | 
			
		||||
 | 
			
		||||
  export const Webview: any;
 | 
			
		||||
 | 
			
		||||
  export const WebviewKeepAlive: any;
 | 
			
		||||
 | 
			
		||||
  export const Card: any;
 | 
			
		||||
 | 
			
		||||
  export const Problem: any;
 | 
			
		||||
 | 
			
		||||
  export const JumpToButton: any;
 | 
			
		||||
 | 
			
		||||
  export const JumpToGroupPanelButton: any;
 | 
			
		||||
 | 
			
		||||
  export const JumpToConverseButton: any;
 | 
			
		||||
}
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue