--- url: /plugins/develop/notifier.md --- # @koishi/plugin-notifier ## 上下文 API ### ctx.notifier.create(options?) * **options:** Fragment | [Options](#options) 通知选项 * 返回值: [`Notifier`](#notifier) 创建一个通知。如果传入的是字符串或 JSX,则会视为 [`options.content`](#options-content)。 ## 通知选项 {#options} ### options.type * 类型: `'primary' | 'success' | 'warning' | 'danger'` * 默认值: `'primary'` 通知类型。 ### options.content * 类型: `Fragment` * 默认值: `''` 通知内容。 ## 类:Notifier {#notifier} ### notifier.update(options?) * **options:** Fragment | [Options](#options) 通知选项 更新此通知。如果传入的是字符串,则会视为 [`options.content`](#options-content)。 ### notifier.dispose() 移除此通知。 --- --- url: /plugins/adapter/dingtalk.md --- # @koishijs/plugin-adapter-dingtalk ::: tip 如果选择通信方式为 `http`,那么你需要准备一个带有 SSL 证书的公网域名,并将 Koishi [部署到公网](../../manual/recipe/server.md)。 ::: ## 接入方式 1. 前往 [开放平台 > 应用开发 > 钉钉应用](https://open-dev.dingtalk.com/fe/app#/corp/app) 并点击「创建应用」,输入相关信息确定创建 2. 在跳转至的页面,选择添加「机器人」;勾选「机器人配置」,输入相关信息;消息接收模式选择 `Stream 模式` 时,插件的 `protocol` 选择 `ws`,选择 `HTTP 模式` 时,将机器人的 `selfUrl` 值后连接 `/dingtalk` (如 `https://example.com/dingtalk`) 填入钉钉平台的消息接收地址,插件的 `protocol` 选择 `http`;最后点击发布按钮 3. 在左侧打开「凭证与基础信息」页面,将 `Client ID` 填入插件的 `appkey` 字段,将 `Client Secret` 填入插件的 `secret` 字段。 4. (可选) 为了在控制台中显示应用名称与头像,请参照 [添加接口调用权限](https://open.dingtalk.com/document/orgapp/add-api-permission) 操作,申请 `qyapi_get_microapp_list` 权限;在左侧打开「添加应用能力」,选择添加「网页应用」。将「企业内部应用 AgentId」填入插件的 `agentId` 字段 ## 配置项 ### config.protocol * 可选值: http, ws 要使用的协议类型。 ### config.secret * 类型: `string` * 必需选项 机器人密钥。 ### config.appkey * 类型: `string` * 必需选项 机器人 AppKey。 ### config.agentId * 类型: `string` * 必需选项 机器人 AgentId。 ## HTTP 配置项 无。 ## WS 配置项 包括全部的 [`WsClient`](../../api/core/adapter.md#类-adapter-wsclient) 选项。 ## 内部接口 ::: tip 关于内部接口的使用方式,请参见 [访问内部接口](../../guide/adapter/bot.md#access-from-plugin)。 ::: * [`internal.addAttendee()`](https://open.dingtalk.com/document/isvapp/add-schedule-participant) * [`internal.addCityCarApply()`](https://open.dingtalk.com/document/isvapp/synchronize-third-party-city-vehicle-approval-form) * [`internal.addCrmPersonalCustomer()`](https://open.dingtalk.com/document/orgapp/add-crm-personal-customers) * [`internal.addCustomSpace()`](https://open.dingtalk.com/document/isvapp/new-custom-space) * [`internal.addFolder()`](https://open.dingtalk.com/document/isvapp/add-folder) * [`internal.addGroupMember()`](https://open.dingtalk.com/document/isvapp/add-group-members) * [`internal.addMeetingRooms()`](https://open.dingtalk.com/document/app/add-a-meeting-room) * [`internal.addMemberToServiceGroup()`](https://open.dingtalk.com/document/orgapp/add-service-group-members) * [`internal.addPoint()`](https://open.dingtalk.com/document/isvapp/increase-or-decrease-resident-points) * [`internal.addProjectMember()`](https://open.dingtalk.com/document/isvapp/add-project-members) * [`internal.addRecentUserAppList()`](https://open.dingtalk.com/document/app/add-recently-used-apps-in-bulk) * [`internal.appendSpace()`](https://open.dingtalk.com/document/orgapp/add-field-interface) * [`internal.approvalList()`](https://open.dingtalk.com/document/isvapp/obtains-the-print-approval-list-for-process-tasks) * [`internal.approveCityCarApply()`](https://open.dingtalk.com/document/isvapp/approval-of-third-party-city-car-application-form) * [`internal.archiveProject()`](https://open.dingtalk.com/document/orgapp/items-in-recycle-bin) * [`internal.archiveTask()`](https://open.dingtalk.com/document/orgapp/archive-tasks) * [`internal.banOrOpenGroupWords()`](https://open.dingtalk.com/document/isvapp/exclusive-dingtalk-group-ban) * [`internal.batchAddContacts()`](https://open.dingtalk.com/document/orgapp/add-contact-data-in-batches) * [`internal.batchAddFollowRecords()`](https://open.dingtalk.com/document/orgapp/batch-add-follow-up-record-data) * [`internal.batchAddRelationDatas()`](https://open.dingtalk.com/document/orgapp/add-multiple-relationship-data-in-batches) * [`internal.batchApproveUnionApply()`](https://open.dingtalk.com/document/isvapp/apply-for-association-with-multiple-branches-in-batch-batch-through-the-application-of-partner-organizations-to-join-contact) * [`internal.batchGetFormDataByIdList()`](https://open.dingtalk.com/document/isvapp/obtain-multiple-form-instance-data) * [`internal.batchInsertBizObject()`](https://open.dingtalk.com/document/isvapp/batch-add-form-business-data) * [`internal.batchOTOQuery()`](https://open.dingtalk.com/document/orgapp/chatbot-batch-query-the-read-status-of-messages) * [`internal.batchQueryGroupMember()`](https://open.dingtalk.com/document/orgapp/query-group-members) * [`internal.batchRecallOTO()`](https://open.dingtalk.com/document/orgapp/batch-message-recall-chat) * [`internal.batchRecallPrivateChat()`](https://open.dingtalk.com/document/orgapp/batch-withdrawal-of-single-chat-robot-messages-in-person-to-person-conversations) * [`internal.batchRemovalByFormInstanceIdList()`](https://open.dingtalk.com/document/isvapp/delete-multiple-form-instances) * [`internal.batchRemoveFollowRecords()`](https://open.dingtalk.com/document/orgapp/batch-delete-follow-up-record-data) * [`internal.batchSaveFormData()`](https://open.dingtalk.com/document/isvapp/create-multiple-form-instances) * [`internal.batchSendOTO()`](https://open.dingtalk.com/document/orgapp/chatbots-send-one-on-one-chat-messages-in-batches) * [`internal.batchSendOfficialAccountOTOMessage()`](https://open.dingtalk.com/document/isvapp/batch-sending-of-service-window-messages) * [`internal.batchUpdateContacts()`](https://open.dingtalk.com/document/orgapp/modify-contact-data-in-batches) * [`internal.batchUpdateFollowRecords()`](https://open.dingtalk.com/document/orgapp/batch-update-follow-up-record-data) * [`internal.batchUpdateFormDataByInstanceId()`](https://open.dingtalk.com/document/isvapp/batch-update-of-component-values-in-form-instances) * [`internal.batchUpdateFormDataByInstanceMap()`](https://open.dingtalk.com/document/isvapp/batch-update-of-form-instances-through-form-component-data) * [`internal.batchUpdateRelationDatas()`](https://open.dingtalk.com/document/orgapp/update-multiple-relational-data-tables-at-a-time) * [`internal.billSettementBtripTrain()`](https://open.dingtalk.com/document/isvapp/business-travel-train-ticket-settlement-bookkeeping-query-interface) * [`internal.billSettementCar()`](https://open.dingtalk.com/document/isvapp/query-interface-for-vehicle-settlement-and-bookkeeping) * [`internal.billSettementFlight()`](https://open.dingtalk.com/document/isvapp/ticket-settlement-bookkeeping-query-interface) * [`internal.billSettementHotel()`](https://open.dingtalk.com/document/isvapp/hotel-settlement-bookkeeping-query-interface) * [`internal.campusCreateCampus()`](https://open.dingtalk.com/document/isvapp/create-a-campus-project) * [`internal.campusCreateCampusGroup()`](https://open.dingtalk.com/document/isvapp/create-a-project-group) * [`internal.campusDeleteCampusGroup()`](https://open.dingtalk.com/document/isvapp/delete-a-project-group) * [`internal.campusGetCampus()`](https://open.dingtalk.com/document/isvapp/query-a-project-in-a-specified-campus) * [`internal.campusGetCampusGroup()`](https://open.dingtalk.com/document/isvapp/query-a-project-group-in-the-specified-park) * [`internal.cancelProcessInstance()`](https://open.dingtalk.com/document/isvapp/cancel-a-process-instance) * [`internal.cancelScheduleConference()`](https://open.dingtalk.com/document/app/cancel-appointment-meeting) * [`internal.changeGroupOwner()`](https://open.dingtalk.com/document/isvapp/change-group-owner) * [`internal.chatIdToOpenConversationId()`](https://open.dingtalk.com/document/orgapp/obtain-group-openconversationid) * [`internal.chatSubAdminUpdate()`](https://open.dingtalk.com/document/orgapp/batch-setup-group-administrator) * [`internal.clear()`](https://open.dingtalk.com/document/app/clear-all) * [`internal.clearData()`](https://open.dingtalk.com/document/app/clear-cell-data) * [`internal.clearRecycleBin()`](https://open.dingtalk.com/document/isvapp/empty-the-recycle-bin) * [`internal.clearRobotPlugin()`](https://open.dingtalk.com/document/orgapp/clear-single-chat-robot-quick-entry) * [`internal.closeTopbox()`](https://open.dingtalk.com/document/orgapp/close-interactive-card-ceiling) * [`internal.closeVideoConference()`](https://open.dingtalk.com/document/isvapp/close-audio-video-conferencing) * [`internal.cohosts()`](https://open.dingtalk.com/document/orgapp/set-up-co-hosts) * [`internal.convFileGetSpace()`](https://open.dingtalk.com/document/orgapp/obtain-group-storage-space-information) * [`internal.copyDentries()`](https://open.dingtalk.com/document/isvapp/copy-files-or-folders-in-bulk) * [`internal.copyDentry()`](https://open.dingtalk.com/document/isvapp/copy-an-object) * [`internal.createAcls()`](https://open.dingtalk.com/document/personalapp/create-access-control) * [`internal.createAndDeliver()`](https://open.dingtalk.com/document/orgapp/create-and-deliver-cards) * [`internal.createBadgeCodeUserInstance()`](https://open.dingtalk.com/document/isvapp/create-a-badge-user-instance) * [`internal.createBadgeNotify()`](https://open.dingtalk.com/document/isvapp/dingtalk-badge-notification-message) * [`internal.createBizObject()`](https://open.dingtalk.com/document/isvapp/create-form-business-data) * [`internal.createCard()`](https://open.dingtalk.com/document/orgapp/interface-for-creating-a-card-instance) * [`internal.createCoupleGroup()`](https://open.dingtalk.com/document/app/creating-two-groups-of-people) * [`internal.createCoupleGroupConversation()`](https://open.dingtalk.com/document/isvapp/create-two-people-outside-the-nail) * [`internal.createEvent()`](https://open.dingtalk.com/document/personalapp/create-schedule) * [`internal.createGroupConversation()`](https://open.dingtalk.com/document/isvapp/create-a-common-group-or-cross-nail-group) * [`internal.createGroupSet()`](https://open.dingtalk.com/document/isvapp/create-a-customer-group-set) * [`internal.createInnerApp()`](https://open.dingtalk.com/document/app/create-an-h5-application-for-your-enterprise) * [`internal.createInterconnection()`](https://open.dingtalk.com/document/isvapp/create-bc-account-association) * [`internal.createJsapiTicket()`](https://open.dingtalk.com/document/isvapp/create-a-jsapi-ticket) * [`internal.createLive()`](https://open.dingtalk.com/document/isvapp/create-live-streaming) * [`internal.createMeetingRoom()`](https://open.dingtalk.com/document/isvapp/create-a-meeting-room) * [`internal.createMeetingRoomGroup()`](https://open.dingtalk.com/document/isvapp/create-a-meeting-room-group) * [`internal.createOrUpdateFormData()`](https://open.dingtalk.com/document/isvapp/add-or-update-form-instances) * [`internal.createOrganizationTask()`](https://open.dingtalk.com/document/isvapp/create-a-free-task-isv) * [`internal.createPlanTime()`](https://open.dingtalk.com/document/isvapp/create-planned-work) * [`internal.createProcessesInstance()`](https://open.dingtalk.com/document/isvapp/create-a-process-instance) * [`internal.createProject()`](https://open.dingtalk.com/document/orgapp/create-project) * [`internal.createProjectByTemplate()`](https://open.dingtalk.com/document/isvapp/create-a-project-from-a-project-template) * [`internal.createProjectCustomfieldStatus()`](https://open.dingtalk.com/document/orgapp/create-project-custom-fields) * [`internal.createScheduleConference()`](https://open.dingtalk.com/document/app/create-appointment-meeting) * [`internal.createStoreGroupConversation()`](https://open.dingtalk.com/document/isvapp/create-a-store-group) * [`internal.createSubscribedCalendar()`](https://open.dingtalk.com/document/isvapp/create-subscription-calendar) * [`internal.createTask()`](https://open.dingtalk.com/document/isvapp/create-a-project-task) * [`internal.createTaskObjectLink()`](https://open.dingtalk.com/document/isvapp/create-a-linked-object-associated-with-a-task) * [`internal.createTicket()`](https://open.dingtalk.com/document/isvapp/smart-customer-service-create-a-self-service-order) * [`internal.createTodoTask()`](https://open.dingtalk.com/document/isvapp/add-dingtalk-to-do-task) * [`internal.createTopbox()`](https://open.dingtalk.com/document/orgapp/create-and-open-an-interactive-card-ceiling) * [`internal.createTrustedDeviceBatch()`](https://open.dingtalk.com/document/isvapp/add-trusted-devices-in-batches) * [`internal.createVideoConference()`](https://open.dingtalk.com/document/isvapp/create-a-video-conference) * [`internal.createWorkTime()`](https://open.dingtalk.com/document/isvapp/create-actual-work) * [`internal.crmCreateGroup()`](https://open.dingtalk.com/document/isvapp/create-a-customer-group) * [`internal.crmUpdateGroupSet()`](https://open.dingtalk.com/document/orgapp/crm-update-group) * [`internal.decodeBadgeCode()`](https://open.dingtalk.com/document/isvapp/stack-dingtalk-badge) * [`internal.deleteAcl()`](https://open.dingtalk.com/document/personalapp/delete-access-control) * [`internal.deleteBizObject()`](https://open.dingtalk.com/document/isvapp/delete-a-business-object) * [`internal.deleteColumns()`](https://open.dingtalk.com/document/app/delete-column) * [`internal.deleteCrmCustomObjectData()`](https://open.dingtalk.com/document/orgapp/delete-crm-custom-object-data) * [`internal.deleteCrmPersonalCustomer()`](https://open.dingtalk.com/document/orgapp/delete-crm-personal-customer) * [`internal.deleteDentries()`](https://open.dingtalk.com/document/isvapp/delete-files-or-folders-in-bulk) * [`internal.deleteDentry()`](https://open.dingtalk.com/document/isvapp/delete-a-file-or-folder) * [`internal.deleteDentryAppProperties()`](https://open.dingtalk.com/document/isvapp/delete-file-app-attribute) * [`internal.deleteEvent()`](https://open.dingtalk.com/document/isvapp/delete-schedule) * [`internal.deleteFormData()`](https://open.dingtalk.com/document/app/delete-form-data) * [`internal.deleteInnerApp()`](https://open.dingtalk.com/document/app/delete-an-internal-h5-application) * [`internal.deleteInstance()`](https://open.dingtalk.com/document/app/delete-process-instance) * [`internal.deleteLive()`](https://open.dingtalk.com/document/isvapp/delete-live-streaming) * [`internal.deleteMeetingRoom()`](https://open.dingtalk.com/document/isvapp/delete-a-meeting-room) * [`internal.deleteMeetingRoomGroup()`](https://open.dingtalk.com/document/isvapp/delete-a-conference-room-group) * [`internal.deleteProcessesInstance()`](https://open.dingtalk.com/document/isvapp/delete-process-instance-data) * [`internal.deleteProjectMember()`](https://open.dingtalk.com/document/orgapp/delete-project-members) * [`internal.deleteRecycleItem()`](https://open.dingtalk.com/document/isvapp/delete-recycle-item) * [`internal.deleteRecycleItems()`](https://open.dingtalk.com/document/isvapp/batch-delete-recycle-items) * [`internal.deleteRows()`](https://open.dingtalk.com/document/app/delete-row) * [`internal.deleteSequence()`](https://open.dingtalk.com/document/isvapp-server/delete-sequence) * [`internal.deleteSpace()`](https://open.dingtalk.com/document/isvapp/delete-a-space) * [`internal.deleteSubscribedCalendar()`](https://open.dingtalk.com/document/isvapp/delete-subscription-calendar) * [`internal.deleteTask()`](https://open.dingtalk.com/document/orgapp/delete-task) * [`internal.deleteTodoTask()`](https://open.dingtalk.com/document/isvapp/delete-dingtalk-to-do-tasks) * [`internal.deliverCard()`](https://open.dingtalk.com/document/isvapp/delivery-card-interface) * [`internal.describeCrmPersonalCustomerObjectMeta()`](https://open.dingtalk.com/document/isvapp/get-metadata-description-of-crm-customer-object-1) * [`internal.dismissGroupConversation()`](https://open.dingtalk.com/document/isvapp/disband-bc-interconnection-group) * [`internal.driveAddSpace()`](https://open.dingtalk.com/document/isvapp/new-space) * [`internal.editContact()`](https://open.dingtalk.com/document/isvapp/add-or-edit-contacts) * [`internal.editCustomer()`](https://open.dingtalk.com/document/isvapp/add-or-edit-customer-profile) * [`internal.editCustomerPool()`](https://open.dingtalk.com/document/isvapp/add-or-edit-customer-public-pools) * [`internal.editExchange()`](https://open.dingtalk.com/document/isvapp/add-or-edit-a-sales-order) * [`internal.editGoods()`](https://open.dingtalk.com/document/isvapp/add-or-edit-product-information) * [`internal.editIntostock()`](https://open.dingtalk.com/document/isvapp/add-or-edit-a-shipment-record) * [`internal.editInvoice()`](https://open.dingtalk.com/document/isvapp/add-or-edit-invoices) * [`internal.editOrder()`](https://open.dingtalk.com/document/isvapp/add-or-edit-contract-orders) * [`internal.editOutstock()`](https://open.dingtalk.com/document/isvapp/add-or-edit-an-issue-ticket) * [`internal.editProduction()`](https://open.dingtalk.com/document/isvapp/add-or-edit-a-production-order) * [`internal.editPurchase()`](https://open.dingtalk.com/document/isvapp/edit-purchase-order) * [`internal.executeActivity()`](https://open.dingtalk.com/document/isvapp/intelligent-customer-service-execute-work-order-activities) * [`internal.executeBatchTask()`](https://open.dingtalk.com/document/app/bulk-approval) * [`internal.executeCustomApi()`](https://open.dingtalk.com/document/isvapp-server/run-custom-api) * [`internal.executePlatformTask()`](https://open.dingtalk.com/document/isvapp-server/execute-appropriate-approval-tasks) * [`internal.executeTask()`](https://open.dingtalk.com/document/app/execute-approval-tasks) * [`internal.expireCommodity()`](https://open.dingtalk.com/document/isvapp-server/make-goods-expire) * [`internal.focus()`](https://open.dingtalk.com/document/orgapp/set-the-whole-staff-to-see-him) * [`internal.getAccessToken()`](https://open.dingtalk.com/document/orgapp/obtain-the-access_token-of-an-internal-app) * [`internal.getActivityButtonList()`](https://open.dingtalk.com/document/isvapp-server/obtain-a-list-of-process-node-buttons-1) * [`internal.getActivityList()`](https://open.dingtalk.com/document/isvapp-server/obtain-the-information-about-the-nodes-in-process-design-1) * [`internal.getAdjustments()`](https://open.dingtalk.com/document/isvapp/retrieve-a-list-of-replenishment-rules-by-page) * [`internal.getAllLabelableDepts()`](https://open.dingtalk.com/document/isvapp/obtains-a-list-of-departments-that-can-be-marked) * [`internal.getApplicationAuthorizationServicePlatformResource()`](https://open.dingtalk.com/document/isvapp-server/obtain-platform-service-resources) * [`internal.getApps()`](https://open.dingtalk.com/document/isvapp/queries-applications) * [`internal.getAttachmentTemporaryUrl()`](https://open.dingtalk.com/document/isvapp/obtain-the-temporary-attachment-free-address) * [`internal.getBotListInGroup()`](https://open.dingtalk.com/document/orgapp/obtain-the-list-of-robots-in-the-group) * [`internal.getConfBaseInfoByLogicalId()`](https://open.dingtalk.com/document/isvapp/you-can-call-this-operation-to-query-the-basic-information) * [`internal.getConversationUrl()`](https://open.dingtalk.com/document/isvapp/obtain-the-session-address) * [`internal.getCorpAccessToken()`](https://open.dingtalk.com/document/isvapp/obtain-the-access_token-of-the-authorized-enterprise) * [`internal.getCorpAccomplishmentTasks()`](https://open.dingtalk.com/document/app/obtains-the-completed-approval-tasks-in-an-organization) * [`internal.getCorpLevelByAccountId()`](https://open.dingtalk.com/document/isvapp-server/query-enterprise-level) * [`internal.getCorpTasks()`](https://open.dingtalk.com/document/app/query-tasks-from-the-organization-dimension) * [`internal.getCrmGroupChatMulti()`](https://open.dingtalk.com/document/isvapp/query-customer-groups-in-batches) * [`internal.getCrmGroupChatSingle()`](https://open.dingtalk.com/document/isvapp/obtain-a-single-customer-group) * [`internal.getCurrentApp()`](https://open.dingtalk.com/document/isvapp/queries-application-information-1) * [`internal.getDataList()`](https://open.dingtalk.com/document/isvapp/obtain-the-data-list) * [`internal.getDataView()`](https://open.dingtalk.com/document/isvapp/queries-data-details) * [`internal.getDentries()`](https://open.dingtalk.com/document/isvapp/get-file-or-folder-information-in-bulk) * [`internal.getDentry()`](https://open.dingtalk.com/document/isvapp/obtain-file-or-folder-information) * [`internal.getDentryOpenInfo()`](https://open.dingtalk.com/document/isvapp/obtains-the-object-preview-or-editing-information) * [`internal.getDentryThumbnails()`](https://open.dingtalk.com/document/isvapp/get-file-thumbnails-in-bulk) * [`internal.getEvent()`](https://open.dingtalk.com/document/personalapp/query-a-single-schedule) * [`internal.getFieldDefByUuid()`](https://open.dingtalk.com/document/isvapp/get-form-field-information-based-on-form-uuid) * [`internal.getFileDownloadInfo()`](https://open.dingtalk.com/document/orgapp/obtains-the-download-information-about-a-file) * [`internal.getFlightExceedApply()`](https://open.dingtalk.com/document/isvapp/dingtalk-oapi-alitrip-btrip-exceedapply-flight-get) * [`internal.getFlowDocs()`](https://open.dingtalk.com/document/isvapp/get-a-list-of-all-contracts-for-the-process-task) * [`internal.getFollowerInfo()`](https://open.dingtalk.com/document/orgapp/queries-the-follower-information-of-the-service-window) * [`internal.getFormComponentDefinitionList()`](https://open.dingtalk.com/document/app/get-a-list-of-form-component-definitions) * [`internal.getFormDataByID()`](https://open.dingtalk.com/document/app/query-form-data) * [`internal.getFormInstance()`](https://open.dingtalk.com/document/isvapp/obtains-the-instance-details-of-a-single-fill-table) * [`internal.getFormListInApp()`](https://open.dingtalk.com/document/app/depending-on-the-application-id-to-get-the-form-list) * [`internal.getGroupSet()`](https://open.dingtalk.com/document/isvapp/queries-the-details-of-a-single-customer-group) * [`internal.getHotelExceedApply()`](https://open.dingtalk.com/document/isvapp/dingtalk-oapi-alitrip-btrip-exceedapply-hotel-get) * [`internal.getInstanceById()`](https://open.dingtalk.com/document/app/queries-a-process-instance-based-on-its-id) * [`internal.getInstanceIdList()`](https://open.dingtalk.com/document/app/obtains-a-list-of-instance-ids) * [`internal.getInstances()`](https://open.dingtalk.com/document/app/obtain-process-instance) * [`internal.getInstancesByIdList()`](https://open.dingtalk.com/document/app/queries-multiple-process-instances) * [`internal.getMeCorpSubmission()`](https://open.dingtalk.com/document/app/obtains-the-tasks-submitted-by-someone-in-an-organization) * [`internal.getMeetingRoomsSchedule()`](https://open.dingtalk.com/document/isvapp/queries-free-and-busy-meeting-room-information) * [`internal.getMicroAppScope()`](https://open.dingtalk.com/document/orgapp/obtains-the-application-visible-range) * [`internal.getMultipartFileUploadInfos()`](https://open.dingtalk.com/document/isvapp/obtains-the-information-about-multipart-uploads-of-an-object) * [`internal.getNode()`](https://open.dingtalk.com/document/orgapp/get-node) * [`internal.getNodeByUrl()`](https://open.dingtalk.com/document/orgapp/get-node-by-link) * [`internal.getNodes()`](https://open.dingtalk.com/document/orgapp/obtain-nodes-in-batch) * [`internal.getNotifyMe()`](https://open.dingtalk.com/document/app/get-notifications-sent-to-users) * [`internal.getOpenUrl()`](https://open.dingtalk.com/document/isvapp/obtain-the-temporary-free-access-address-of-yixian-accessories) * [`internal.getOperationRecords()`](https://open.dingtalk.com/document/app/queries-an-approval-record) * [`internal.getOrg()`](https://open.dingtalk.com/document/isvapp/obtain-enterprise-information-1) * [`internal.getOrgAuthInfo()`](https://open.dingtalk.com/document/isvapp/obtain-enterprise-authentication-information) * [`internal.getOrganizatioTaskByIds()`](https://open.dingtalk.com/document/isvapp/obtains-details-about-multiple-free-tasks-isv) * [`internal.getOrganizationPriorityList()`](https://open.dingtalk.com/document/isvapp/query-a-priority-list-isv) * [`internal.getOrganizationTask()`](https://open.dingtalk.com/document/isvapp/queries-free-task-details-isv) * [`internal.getOrganizations()`](https://open.dingtalk.com/document/isvapp/queries-organization-data) * [`internal.getOvertimeSetting()`](https://open.dingtalk.com/document/isvapp/batch-retrieve-overtime-rules) * [`internal.getPartnerTypeByParentId()`](https://open.dingtalk.com/document/isvapp/obtain-child-tags-from-a-parent-tag) * [`internal.getPermissionInheritance()`](https://open.dingtalk.com/document/orgapp/get-permission-inheritance-mode) * [`internal.getPersonalAuthRule()`](https://open.dingtalk.com/document/isvapp/query-individual-authorization-records) * [`internal.getPluginPermissionPoint()`](https://open.dingtalk.com/document/dashboard/obtain-the-permissions-of-the-workbench-plug-in) * [`internal.getPluginRuleCheckInfo()`](https://open.dingtalk.com/document/dashboard/you-can-call-this-operation-to-obtain-the-information-about) * [`internal.getProcessDefinition()`](https://open.dingtalk.com/document/isvapp-server/obtain-process-definition) * [`internal.getProjectGroup()`](https://open.dingtalk.com/document/isvapp/query-available-project-groups) * [`internal.getProjectMemebers()`](https://open.dingtalk.com/document/orgapp/get-project-members) * [`internal.getProjectStatusList()`](https://open.dingtalk.com/document/orgapp/query-project-status) * [`internal.getPublicDevices()`](https://open.dingtalk.com/document/orgapp/query-public-equipment) * [`internal.getRecycleBin()`](https://open.dingtalk.com/document/isvapp/obtain-information-about-the-recycle-bin) * [`internal.getRecycleItem()`](https://open.dingtalk.com/document/isvapp/obtain-recycling-item-information) * [`internal.getRelationUkSetting()`](https://open.dingtalk.com/document/isvapp/obtain-duplicate-check-fields) * [`internal.getRoleUsers()`](https://open.dingtalk.com/document/isvapp/obtain-role-data-1) * [`internal.getRoles()`](https://open.dingtalk.com/document/isvapp/obtain-role-data) * [`internal.getRunningTasks()`](https://open.dingtalk.com/document/app/query-process-running-tasks-vpc) * [`internal.getSaleUserInfoByUserId()`](https://open.dingtalk.com/document/isvapp-server/query-sales-user-information) * [`internal.getSceneGroupInfo()`](https://open.dingtalk.com/document/isvapp/query-group-brief-information) * [`internal.getSchedule()`](https://open.dingtalk.com/document/isvapp/free-schedule) * [`internal.getSignInList()`](https://open.dingtalk.com/document/isvapp/view-the-check-in-details-of-a-single-schedule) * [`internal.getSignOutList()`](https://open.dingtalk.com/document/isvapp/view-the-billing-details-of-a-single-schedule) * [`internal.getSignedDetailByPage()`](https://open.dingtalk.com/document/isvapp/obtains-the-information-about-the-persons-who-sign-the-audit) * [`internal.getSimpleOvertimeSetting()`](https://open.dingtalk.com/document/isvapp/retrieve-a-list-of-overtime-rules-by-page) * [`internal.getSsoAccessToken()`](https://open.dingtalk.com/document/isvapp/obtain-the-access_token-of-the-micro-application-background-without-log-on) * [`internal.getSsoUserInfo()`](https://open.dingtalk.com/document/isvapp/obtains-the-identity-of-an-application-administrator) * [`internal.getSubscribedCalendar()`](https://open.dingtalk.com/document/isvapp/query-a-single-subscription-calendar) * [`internal.getTask()`](https://open.dingtalk.com/document/isvapp/get-the-asynchronous-task-information-in-storage) * [`internal.getTaskByIds()`](https://open.dingtalk.com/document/orgapp/get-task-details) * [`internal.getTaskCopies()`](https://open.dingtalk.com/document/app/query-copied-my-task-list-application-dimension) * [`internal.getTbOrgIdByDingOrgId()`](https://open.dingtalk.com/document/isvapp/obtain-the-teambition-enterprise-id) * [`internal.getTbUserIdByStaffId()`](https://open.dingtalk.com/document/isvapp/obtain-dingtalk-teambition-user-id-based-on-userid) * [`internal.getTrainExceedApply()`](https://open.dingtalk.com/document/isvapp/dingtalk-oapi-alitrip-btrip-exceedapply-train-get) * [`internal.getUploadUrl()`](https://open.dingtalk.com/document/isvapp/obtain-the-upload-url-of-a-file-2) * [`internal.getUserFollowStatus()`](https://open.dingtalk.com/document/orgapp/obtain-the-attention-status-of-the-user-service-window) * [`internal.getUserJoinedProject()`](https://open.dingtalk.com/document/orgapp/get-projects-joined-by-users) * [`internal.getUserToken()`](https://open.dingtalk.com/document/isvapp/obtain-user-token) * [`internal.getUsers()`](https://open.dingtalk.com/document/isvapp/obtain-user-data) * [`internal.hrmProcessRegular()`](https://open.dingtalk.com/document/orgapp/intelligent-personnel-staff-to-become-regular) * [`internal.hrmProcessTransfer()`](https://open.dingtalk.com/document/orgapp/intelligent-personnel-staff-transfer) * [`internal.hrmProcessUpdateTerminationInfo()`](https://open.dingtalk.com/document/app/modify-resigned-employee-information) * [`internal.imCreateGroup()`](https://open.dingtalk.com/document/app/create-common-group-new-version) * [`internal.initMultipartFileUpload()`](https://open.dingtalk.com/document/isvapp/initialize-a-multipart-upload-object) * [`internal.insertColumnsBefore()`](https://open.dingtalk.com/document/app/insert-column-before-column) * [`internal.insertRowsBefore()`](https://open.dingtalk.com/document/app/insert-rows-before-rows) * [`internal.inviteUsers()`](https://open.dingtalk.com/document/orgapp/invite-users-to-join) * [`internal.listAccount()`](https://open.dingtalk.com/document/orgapp/queries-the-list-of-services-under-an-enterprise) * [`internal.listAcls()`](https://open.dingtalk.com/document/personalapp/get-access-control-list) * [`internal.listActivateDevices()`](https://open.dingtalk.com/document/isvapp/query-information-about-a-registered-device) * [`internal.listAllApp()`](https://open.dingtalk.com/document/orgapp/obtains-a-list-of-all-enterprise-applications) * [`internal.listAllDentries()`](https://open.dingtalk.com/document/isvapp/get-the-list-of-files-or-folders-under-a-space) * [`internal.listAllInnerApps()`](https://open.dingtalk.com/document/orgapp/get-a-list-of-all-applications-inside-the-enterprise) * [`internal.listApplication()`](https://open.dingtalk.com/document/isvapp/query-the-application-list) * [`internal.listAttendees()`](https://open.dingtalk.com/document/isvapp/query-schedule-participants) * [`internal.listAuditLog()`](https://open.dingtalk.com/document/app/queries-file-audit-logs) * [`internal.listCalendars()`](https://open.dingtalk.com/document/isvapp/query-the-calendar) * [`internal.listCrmPersonalCustomers()`](https://open.dingtalk.com/document/isvapp/acquire-crm-individual-customers-in-batches) * [`internal.listDentries()`](https://open.dingtalk.com/document/isvapp/obtain-the-file-list-storage) * [`internal.listDentryVersions()`](https://open.dingtalk.com/document/isvapp/obtains-a-list-of-file-versions) * [`internal.listEvents()`](https://open.dingtalk.com/document/personalapp/query-an-event-list-1) * [`internal.listEventsView()`](https://open.dingtalk.com/document/personalapp/query-schedule-view-1) * [`internal.listFollower()`](https://open.dingtalk.com/document/orgapp/obtains-the-follower-information-from-the-service-window) * [`internal.listFormInstances()`](https://open.dingtalk.com/document/isvapp/obtain-the-table-filling-instance-list-data) * [`internal.listFormRemarks()`](https://open.dingtalk.com/document/isvapp/batch-query-of-comments-appropriate-for-form-instances) * [`internal.listFormSchemasByCreator()`](https://open.dingtalk.com/document/isvapp/new-obtains-the-template-that-a-user-creates) * [`internal.listGroupSet()`](https://open.dingtalk.com/document/isvapp/query-the-list-of-customer-groups-set) * [`internal.listIndustryRoleUsers()`](https://open.dingtalk.com/document/isvapp/obtains-a-list-of-users-under-an-industry-role) * [`internal.listInnerAppVersion()`](https://open.dingtalk.com/document/orgapp/get-the-version-list-of-the-enterprise-internal-applet) * [`internal.listInspectInfo()`](https://open.dingtalk.com/document/isvapp/obtain-inspection-and-maintenance-records) * [`internal.listMaintainInfo()`](https://open.dingtalk.com/document/isvapp/obtain-the-repair-report-record) * [`internal.listNavigationByFormType()`](https://open.dingtalk.com/document/isvapp-server/obtains-the-page-list-under-an-application) * [`internal.listNodes()`](https://open.dingtalk.com/document/orgapp/get-node-list) * [`internal.listOperationLogs()`](https://open.dingtalk.com/document/app/query-the-operation-records-of-a-form) * [`internal.listPermissionsIsv()`](https://open.dingtalk.com/document/isvapp/obtain-a-permission-list-storage) * [`internal.listPermissionsOrg()`](https://open.dingtalk.com/document/orgapp/get-permission-list) * [`internal.listPointRules()`](https://open.dingtalk.com/document/isvapp/query-all-credit-rules) * [`internal.listRecycleItems()`](https://open.dingtalk.com/document/isvapp/gets-the-list-of-recycle-items) * [`internal.listSpaces()`](https://open.dingtalk.com/document/isvapp/queries-a-space-list) * [`internal.listTableDataByFormInstanceIdTableId()`](https://open.dingtalk.com/document/app/obtain-child-table-component-data) * [`internal.listUserIndustryRoles()`](https://open.dingtalk.com/document/isvapp/obtains-information-about-the-industry-role-to-which-the-user) * [`internal.listUserVilebleApp()`](https://open.dingtalk.com/document/orgapp/obtains-the-list-of-enterprise-applications-visible-to-a-user) * [`internal.loadBizFields()`](https://open.dingtalk.com/document/isvapp/gets-the-form-object-structure) * [`internal.loadBizObject()`](https://open.dingtalk.com/document/isvapp/queries-business-instance-information) * [`internal.loadBizObjects()`](https://open.dingtalk.com/document/isvapp/querying-form-business-data) * [`internal.moveDentries()`](https://open.dingtalk.com/document/isvapp/bulk-move-files-or-folders) * [`internal.moveDentry()`](https://open.dingtalk.com/document/isvapp/move-a-file-or-folder) * [`internal.moveStudent()`](https://open.dingtalk.com/document/isvapp/shift-students) * [`internal.muteMembers()`](https://open.dingtalk.com/document/orgapp/specify-person-to-mute-or-unmute) * [`internal.notifyBadgeCodePayResult()`](https://open.dingtalk.com/document/isvapp/sync-dingtalk-badge-code-payment-result) * [`internal.notifyBadgeCodeRefundResult()`](https://open.dingtalk.com/document/isvapp/notification-dingtalk-badge-code-refund-result) * [`internal.notifyBadgeCodeVerifyResult()`](https://open.dingtalk.com/document/isvapp/notification-dingtalk-badge-verification-result) * [`internal.oapiAiMtTranslate()`](https://open.dingtalk.com/document/isvapp/dingtalk-translation) * [`internal.oapiAlitripBtripAddressGet()`](https://open.dingtalk.com/document/isvapp/obtain-business-travel-access-addresses) * [`internal.oapiAlitripBtripApplyGet()`](https://open.dingtalk.com/document/isvapp/obtains-the-detailed-data-of-a-single-request) * [`internal.oapiAlitripBtripApplySearch()`](https://open.dingtalk.com/document/isvapp/search-enterprise-approval-form-data) * [`internal.oapiAlitripBtripApprovalModify()`](https://open.dingtalk.com/document/isvapp/user-modify-approval-form) * [`internal.oapiAlitripBtripApprovalNew()`](https://open.dingtalk.com/document/isvapp/user-new-approval-form) * [`internal.oapiAlitripBtripApprovalUpdate()`](https://open.dingtalk.com/document/isvapp/update-approval-form) * [`internal.oapiAlitripBtripCostCenterDelete()`](https://open.dingtalk.com/document/isvapp/delete-cost-center) * [`internal.oapiAlitripBtripCostCenterEntityDelete()`](https://open.dingtalk.com/document/isvapp/delete-cost-center-personnel-information) * [`internal.oapiAlitripBtripCostCenterEntitySet()`](https://open.dingtalk.com/document/isvapp/set-up-cost-center-personnel-information) * [`internal.oapiAlitripBtripCostCenterModify()`](https://open.dingtalk.com/document/isvapp/modify-basic-cost-center-information) * [`internal.oapiAlitripBtripCostCenterNew()`](https://open.dingtalk.com/document/isvapp/new-cost-center) * [`internal.oapiAlitripBtripCostCenterQuery()`](https://open.dingtalk.com/document/isvapp/query-cost-center) * [`internal.oapiAlitripBtripCostCenterTransfer()`](https://open.dingtalk.com/document/isvapp/business-travel-cost-center-converted-to-external-cost-center) * [`internal.oapiAlitripBtripFlightCitySuggest()`](https://open.dingtalk.com/document/isvapp/air-ticket-city-search) * [`internal.oapiAlitripBtripFlightOrderSearch()`](https://open.dingtalk.com/document/isvapp/obtains-enterprise-ticket-order-data) * [`internal.oapiAlitripBtripHotelOrderSearch()`](https://open.dingtalk.com/document/isvapp/obtains-the-order-data-of-enterprise-hotels) * [`internal.oapiAlitripBtripInvoiceSearch()`](https://open.dingtalk.com/document/isvapp/query-available-invoices) * [`internal.oapiAlitripBtripInvoiceSettingAdd()`](https://open.dingtalk.com/document/isvapp/new-invoice-configuration) * [`internal.oapiAlitripBtripInvoiceSettingDelete()`](https://open.dingtalk.com/document/isvapp/delete-invoice-configuration) * [`internal.oapiAlitripBtripInvoiceSettingModify()`](https://open.dingtalk.com/document/isvapp/modify-invoice-configuration) * [`internal.oapiAlitripBtripInvoiceSettingRule()`](https://open.dingtalk.com/document/isvapp/configure-invoice-users) * [`internal.oapiAlitripBtripMonthbillUrlGet()`](https://open.dingtalk.com/document/isvapp/obtain-monthly-reconciliation-settlement-data) * [`internal.oapiAlitripBtripPriceQuery()`](https://open.dingtalk.com/document/isvapp/query-estimated-price) * [`internal.oapiAlitripBtripProjectAdd()`](https://open.dingtalk.com/document/isvapp/add-a-project) * [`internal.oapiAlitripBtripProjectDelete()`](https://open.dingtalk.com/document/isvapp/delete-a-project) * [`internal.oapiAlitripBtripProjectModify()`](https://open.dingtalk.com/document/isvapp/project-change) * [`internal.oapiAlitripBtripTrainCitySuggest()`](https://open.dingtalk.com/document/isvapp/train-ticket-city-search) * [`internal.oapiAlitripBtripTrainOrderSearch()`](https://open.dingtalk.com/document/isvapp/obtains-the-enterprise-train-ticket-order-data) * [`internal.oapiAlitripBtripVehicleOrderSearch()`](https://open.dingtalk.com/document/isvapp/vehicle-order-query-interface) * [`internal.oapiAsrVoiceTranslate()`](https://open.dingtalk.com/document/isvapp/asr-short-sentence-recognition) * [`internal.oapiBlackboardCategoryList()`](https://open.dingtalk.com/document/orgapp/obtains-the-list-of-categories-not-deleted-for-enterprise-announcements) * [`internal.oapiBlackboardCreate()`](https://open.dingtalk.com/document/orgapp/create-an-enterprise-announcement) * [`internal.oapiBlackboardDelete()`](https://open.dingtalk.com/document/orgapp/delete-announcements-based-on-the-announcement-id) * [`internal.oapiBlackboardGet()`](https://open.dingtalk.com/document/orgapp/obtains-the-details-of-a-bulletin-that-is-not-deleted) * [`internal.oapiBlackboardListids()`](https://open.dingtalk.com/document/orgapp/obtains-the-id-list-of-announcements-that-are-not-deleted) * [`internal.oapiBlackboardListtopten()`](https://open.dingtalk.com/document/orgapp/list-the-user-s-announcement-list) * [`internal.oapiBlackboardUpdate()`](https://open.dingtalk.com/document/orgapp/modify-the-announcement-according-to-the-announcement-id) * [`internal.oapiChatCreate()`](https://open.dingtalk.com/document/orgapp/create-group-session) * [`internal.oapiChatGet()`](https://open.dingtalk.com/document/orgapp/obtain-a-group-session) * [`internal.oapiChatMemberFriendswitchUpdate()`](https://open.dingtalk.com/document/orgapp/set-private-chat) * [`internal.oapiChatQrcodeGet()`](https://open.dingtalk.com/document/orgapp/obtain-a-qr-code-link) * [`internal.oapiChatSubadminUpdate()`](https://open.dingtalk.com/document/orgapp/set-chat-admin) * [`internal.oapiChatUpdate()`](https://open.dingtalk.com/document/orgapp/modify-a-group-session) * [`internal.oapiChatUpdategroupnick()`](https://open.dingtalk.com/document/orgapp/set-a-group-nickname) * [`internal.oapiCheckinRecord()`](https://open.dingtalk.com/document/orgapp/get-check-in-data) * [`internal.oapiCheckinRecordGet()`](https://open.dingtalk.com/document/isvapp/obtain-the-check-in-records-of-multiple-users) * [`internal.oapiCrmObjectdataContactDelete()`](https://open.dingtalk.com/document/orgapp/delete-crm-contact) * [`internal.oapiCrmObjectdataContactList()`](https://open.dingtalk.com/document/orgapp/retrieves-contact-data-in-batches-based-on-the-id-list) * [`internal.oapiCrmObjectdataContactQuery()`](https://open.dingtalk.com/document/isvapp/query-contact-data) * [`internal.oapiCrmObjectdataCustomobjectCreate()`](https://open.dingtalk.com/document/orgapp/dingtalk-paas-master-create-custom-crm-object-data) * [`internal.oapiCrmObjectdataCustomobjectUpdate()`](https://open.dingtalk.com/document/orgapp/crm-master-data-opens-interface-for-updating-custom-object-data) * [`internal.oapiCrmObjectdataFollowrecordList()`](https://open.dingtalk.com/document/orgapp/dingtalk-the-primary-data-of-apsara-stack-agility-paas-allows-you) * [`internal.oapiCrmObjectdataFollowrecordQuery()`](https://open.dingtalk.com/document/orgapp/query-and-dingtalk-data-of-track-records-in-apsara-stack) * [`internal.oapiCrmObjectdataList()`](https://open.dingtalk.com/document/orgapp/retrieves-custom-crm-forms-from-the-id-list) * [`internal.oapiCrmObjectdataQuery()`](https://open.dingtalk.com/document/orgapp/retrieve-custom-crm-object-data) * [`internal.oapiCrmObjectmetaContactDescribe()`](https://open.dingtalk.com/document/isvapp/get-metadata-for-a-contact) * [`internal.oapiCrmObjectmetaDescribe()`](https://open.dingtalk.com/document/orgapp/get-metadata-description-of-crm-custom-object) * [`internal.oapiCrmObjectmetaFollowrecordDescribe()`](https://open.dingtalk.com/document/isvapp/get-the-metadata-of-the-follow-up-record-object) * [`internal.oapiCspaceAdd()`](https://open.dingtalk.com/document/isvapp-server/add-file-to-user-s-dingtalk-disk) * [`internal.oapiCspaceAddToSingleChat()`](https://open.dingtalk.com/document/isvapp-server/sends-a-file-to-a-specified-user) * [`internal.oapiCspaceGetCustomSpace()`](https://open.dingtalk.com/document/isvapp-server/obtain-user-space-under-the-enterprise) * [`internal.oapiCspaceGrantCustomSpace()`](https://open.dingtalk.com/document/isvapp-server/authorize-a-user-to-access-a-custom-workspace-of-an) * [`internal.oapiEduCertGet()`](https://open.dingtalk.com/document/isvapp/obtain-digital-certificate) * [`internal.oapiEduUserList()`](https://open.dingtalk.com/document/isvapp/obtains-a-list-of-home-school-user-identities) * [`internal.oapiGetJsapiTicket()`](https://open.dingtalk.com/document/isvapp/obtain-jsapi_ticket) * [`internal.oapiGettoken()`](https://open.dingtalk.com/document/orgapp/obtain-orgapp-token) * [`internal.oapiHealthStepinfoGetuserstatus()`](https://open.dingtalk.com/document/isvapp/check-whether-dingtalk-is-enabled) * [`internal.oapiHealthStepinfoList()`](https://open.dingtalk.com/document/orgapp/queries-individual-or-department-dingtalk-exercise-steps) * [`internal.oapiHealthStepinfoListbyuserid()`](https://open.dingtalk.com/document/orgapp/queries-the-number-of-dingtalk-movement-steps-of-multiple-users) * [`internal.oapiImChatScencegroupInteractivecardCallbackRegister()`](https://open.dingtalk.com/document/orgapp/registration-card-interaction-callback-address-1) * [`internal.oapiImChatScencegroupMessageSendV2()`](https://open.dingtalk.com/document/isvapp/send-group-helper-message) * [`internal.oapiImChatScenegroupCreate()`](https://open.dingtalk.com/document/isvapp/create-group) * [`internal.oapiImChatScenegroupGet()`](https://open.dingtalk.com/document/isvapp/querying-group-information) * [`internal.oapiImChatScenegroupMemberAdd()`](https://open.dingtalk.com/document/isvapp/add-group-members-1) * [`internal.oapiImChatScenegroupMemberDelete()`](https://open.dingtalk.com/document/isvapp/delete-group-members) * [`internal.oapiImChatScenegroupMemberGet()`](https://open.dingtalk.com/document/group/obtains-scene-members) * [`internal.oapiImChatScenegroupTemplateApply()`](https://open.dingtalk.com/document/isvapp/enable-group-template) * [`internal.oapiImChatScenegroupTemplateClose()`](https://open.dingtalk.com/document/isvapp/deactivate-group-template) * [`internal.oapiImChatScenegroupUpdate()`](https://open.dingtalk.com/document/isvapp/update-group) * [`internal.oapiMicroappDelete()`](https://open.dingtalk.com/document/orgapp-server/delete-an-h5-microapplication) * [`internal.oapiMicroappList()`](https://open.dingtalk.com/document/orgapp-server/manager-microapplications-api-permission) * [`internal.oapiMicroappListByUserid()`](https://open.dingtalk.com/document/orgapp-server/list-the-microapplications-visible-to-employees) * [`internal.oapiMicroappSetVisibleScopes()`](https://open.dingtalk.com/document/orgapp-server/set-the-visible-range-of-the-microapplication) * [`internal.oapiMicroappVisibleScopes()`](https://open.dingtalk.com/document/orgapp-server/gets-the-microapplication-visible-range-set-by-the-enterprise) * [`internal.oapiOcrStructuredRecognize()`](https://open.dingtalk.com/document/isvapp/structured-image-recognition-api) * [`internal.oapiOrgUnionTrunkGet()`](https://open.dingtalk.com/document/isvapp/obtain-backbone-organization-list) * [`internal.oapiReportCommentList()`](https://open.dingtalk.com/document/orgapp/queries-log-comment-details) * [`internal.oapiReportCreate()`](https://open.dingtalk.com/document/isvapp/create-a-log) * [`internal.oapiReportGetunreadcount()`](https://open.dingtalk.com/document/orgapp/querying-the-employee-s-log-is-not-reading) * [`internal.oapiReportList()`](https://open.dingtalk.com/document/isvapp/obtains-a-list-of-the-logs-that-are-sent-by) * [`internal.oapiReportReceiverList()`](https://open.dingtalk.com/document/orgapp/queries-log-sharing-personnel) * [`internal.oapiReportSavecontent()`](https://open.dingtalk.com/document/isvapp/save-custom-log-content) * [`internal.oapiReportSimplelist()`](https://open.dingtalk.com/document/orgapp/view-log-summary-data) * [`internal.oapiReportStatistics()`](https://open.dingtalk.com/document/orgapp/query-log-statistics) * [`internal.oapiReportStatisticsListbytype()`](https://open.dingtalk.com/document/orgapp/obtains-a-list-of-log-related-personnel-by-type) * [`internal.oapiReportTemplateGetbyname()`](https://open.dingtalk.com/document/isvapp/query-template-details) * [`internal.oapiReportTemplateListbyuserid()`](https://open.dingtalk.com/document/orgapp/obtains-the-list-of-visible-log-templates-based-on-the) * [`internal.oapiRobotSend()`](https://open.dingtalk.com/document/isvapp/custom-bot-access-send-message) * [`internal.oapiServiceGetAuthInfo()`](https://open.dingtalk.com/document/isvapp/obtains-the-basic-information-of-an-enterprise) * [`internal.oapiServiceGetCorpToken()`](https://open.dingtalk.com/document/isvapp/obtains-the-enterprise-authorized-credential) * [`internal.oapiSmartbotMsgPush()`](https://open.dingtalk.com/document/orgapp/the-message-pushing-interface-of-the-assistant) * [`internal.oapiSmartdeviceDeviceQuery()`](https://open.dingtalk.com/document/isvapp/intelligent-hardware-device-query) * [`internal.oapiSmartdeviceDeviceQuerybyid()`](https://open.dingtalk.com/document/isvapp/the-smart-hardware-can-query-details-based-on-the-device) * [`internal.oapiSmartdeviceDeviceQuerylist()`](https://open.dingtalk.com/document/isvapp/intelligent-hardware-list-query) * [`internal.oapiSmartdeviceDeviceUnbind()`](https://open.dingtalk.com/document/isvapp/unbind-a-smart-hardware-device) * [`internal.oapiSmartdeviceDeviceUpdatenick()`](https://open.dingtalk.com/document/isvapp/intelligent-hardware-device-nickname-modification) * [`internal.oapiSmartdeviceExternalBind()`](https://open.dingtalk.com/document/isvapp/establishing-a-binding-relationship-between-intelligent-hardware-and-cloud) * [`internal.oapiSmartworkHrmEmployeeAddpreentry()`](https://open.dingtalk.com/document/isvapp/add-employees-to-be-hired-through-intelligent-personnel) * [`internal.oapiSmartworkHrmEmployeeFieldGrouplist()`](https://open.dingtalk.com/document/isvapp/get-roster-field-group-details) * [`internal.oapiSmartworkHrmEmployeeList()`](https://open.dingtalk.com/document/isvapp-server/obtaining-employee-roster-field-information) * [`internal.oapiSmartworkHrmEmployeeQueryonjob()`](https://open.dingtalk.com/document/isvapp/intelligent-personnel-query-the-list-of-on-the-job-employees-of-the) * [`internal.oapiSmartworkHrmEmployeeQuerypreentry()`](https://open.dingtalk.com/document/isvapp/intelligent-personnel-query-the-list-of-employees-to-be-hired) * [`internal.oapiSmartworkHrmEmployeeUpdate()`](https://open.dingtalk.com/document/isvapp-server/update-employee-roster) * [`internal.oapiSmartworkHrmEmployeeV2List()`](https://open.dingtalk.com/document/orgapp/intelligent-personnel-obtain-employee-roster-information) * [`internal.oapiSmartworkHrmEmployeeV2Update()`](https://open.dingtalk.com/document/isvapp/intelligent-personnel-update-employee-file-information) * [`internal.oapiSmartworkHrmRosterMetaGet()`](https://open.dingtalk.com/document/isvapp/intelligent-personnel-roster-metadata-query) * [`internal.oapiSnsGetuserinfoBycode()`](https://open.dingtalk.com/document/isvapp-server/obtain-the-user-information-based-on-the-sns-temporary-authorization) * [`internal.oapiSsoGettoken()`](https://open.dingtalk.com/document/isvapp/obtain-the-ssotoken-for-micro-application-background-logon-free) * [`internal.oapiSsoGetuserinfo()`](https://open.dingtalk.com/document/isvapp/exchange-code-for-the-identity-information-of-a-microapplication-administrator) * [`internal.oapiV2UserCreate()`](https://open.dingtalk.com/document/orgapp/user-information-creation) * [`internal.oapiV2UserGetuserinfo()`](https://open.dingtalk.com/document/isvapp/obtain-the-userid-of-a-user-by-using-the-log-free) * [`internal.oapiV2UserUpdate()`](https://open.dingtalk.com/document/orgapp/update-dedicated-accounts-information) * [`internal.oapiWorkspaceAuditlogList()`](https://open.dingtalk.com/document/orgapp/query-file-operation-logs-of-a-project) * [`internal.orgGroupQuery()`](https://open.dingtalk.com/document/orgapp/chatbot-queries-the-read-status-of-a-message) * [`internal.orgGroupRecall()`](https://open.dingtalk.com/document/orgapp/enterprise-chatbot-withdraws-internal-group-messages) * [`internal.orgGroupSend()`](https://open.dingtalk.com/document/orgapp/the-robot-sends-a-group-message) * [`internal.pageInnerAppHistoryVersion()`](https://open.dingtalk.com/document/orgapp/obtain-the-list-of-historical-versions-of-enterprise-internal-applets) * [`internal.pageListAction()`](https://open.dingtalk.com/document/isvapp/intelligent-customer-service-query-action-records) * [`internal.pageListTicket()`](https://open.dingtalk.com/document/isvapp/intelligent-customer-service-paging-query-work-order) * [`internal.pagePointHistory()`](https://open.dingtalk.com/document/isvapp/query-the-integral-flow-records-by-page) * [`internal.patchEvent()`](https://open.dingtalk.com/document/personalapp/modify-schedule) * [`internal.pediaWordsAdd()`](https://open.dingtalk.com/document/app/new-entry) * [`internal.pediaWordsApprove()`](https://open.dingtalk.com/document/app/entry-review) * [`internal.pediaWordsDelete()`](https://open.dingtalk.com/document/app/entry-delete) * [`internal.pediaWordsQuery()`](https://open.dingtalk.com/document/app/entry-query) * [`internal.pediaWordsSearch()`](https://open.dingtalk.com/document/app/entry-search) * [`internal.pediaWordsUpdate()`](https://open.dingtalk.com/document/app/update-entry) * [`internal.privateChatQuery()`](https://open.dingtalk.com/document/orgapp/query-the-read-list-of-robot-messages-in-person-to-person-conversations) * [`internal.privateChatSend()`](https://open.dingtalk.com/document/orgapp/the-robot-sends-ordinary-messages-in-a-person-to-person-conversation) * [`internal.publishFileChangeNotice()`](https://open.dingtalk.com/document/isvapp/send-comments-on-file-changes) * [`internal.publishInnerAppVersion()`](https://open.dingtalk.com/document/orgapp/release-internal-applet-version) * [`internal.pullDataByPage()`](https://open.dingtalk.com/document/connector/bulk-pull-dingtalk-connector-master-data) * [`internal.pullDataByPk()`](https://open.dingtalk.com/document/connector/pull-a-single-primary-record-based-on-the-business-primary) * [`internal.queryActiveUserStatisticalData()`](https://open.dingtalk.com/document/orgapp/obtains-statistics-on-user-activation-status) * [`internal.queryActiveUsers()`](https://open.dingtalk.com/document/orgapp/queries-active-service-users) * [`internal.queryAllCustomer()`](https://open.dingtalk.com/document/orgapp/crm-obtains-all-private-sea-customer-data) * [`internal.queryAppFunctionNodes()`](https://open.dingtalk.com/document/isvapp/queries-the-application-feature-nodes) * [`internal.queryApprovalStatisticalData()`](https://open.dingtalk.com/document/orgapp/obtains-enterprise-approval-statistics) * [`internal.queryAttendanceStatisticalData()`](https://open.dingtalk.com/document/orgapp/queries-enterprise-attendance-statistics) * [`internal.queryBlackboardSpace()`](https://open.dingtalk.com/document/app/obtain-bulletin-nail-disk-space-information) * [`internal.queryBlackboardStatisticalData()`](https://open.dingtalk.com/document/orgapp/queries-corporate-announcement-statistics) * [`internal.queryCalendarStatisticalData()`](https://open.dingtalk.com/document/orgapp/queries-enterprise-schedule-statistics) * [`internal.queryCheckinStatisticalData()`](https://open.dingtalk.com/document/orgapp/queries-enterprise-check-in-statistics) * [`internal.queryCircleStatisticalData()`](https://open.dingtalk.com/document/orgapp/obtains-the-statistical-data-of-all-employees-of-an-enterprise) * [`internal.queryCityCarApply()`](https://open.dingtalk.com/document/isvapp/query-the-application-form-for-third-party-vehicles-in-the-city) * [`internal.queryCloudRecordText()`](https://open.dingtalk.com/document/isvapp/queries-the-text-information-about-cloud-recording) * [`internal.queryCloudRecordVideo()`](https://open.dingtalk.com/document/isvapp/query-recording-information) * [`internal.queryCloudRecordVideoPlayInfo()`](https://open.dingtalk.com/document/isvapp/queries-the-playback-information-about-a-recorded-cloud-video) * [`internal.queryConferenceInfo()`](https://open.dingtalk.com/document/app/querying-video-conference-information) * [`internal.queryConferenceInfoBatch()`](https://open.dingtalk.com/document/isvapp/batch-query-of-video-conference-information) * [`internal.queryConferenceMembers()`](https://open.dingtalk.com/document/app/query-video-meeting-member-information) * [`internal.queryCrmGroupChats()`](https://open.dingtalk.com/document/isvapp/query-a-list-of-customer-groups) * [`internal.queryCrmPersonalCustomer()`](https://open.dingtalk.com/document/isvapp/obtains-crm-individual-customers-in-batches-based-on-specified-query) * [`internal.queryDeviceProperties()`](https://open.dingtalk.com/document/app/querying-video-conference-device-attribute-information) * [`internal.queryDigitalDistrictOrgInfo()`](https://open.dingtalk.com/document/orgapp/querydigitaldistrictorginfo-api-reference) * [`internal.queryDingReciveStatisticalData()`](https://open.dingtalk.com/document/orgapp/obtain-statistics-on-receiving-and-comments-of-enterprise-ding) * [`internal.queryDingSendStatisticalData()`](https://open.dingtalk.com/document/orgapp/obtain-sending-statistics-of-an-enterprise-ding) * [`internal.queryDismissionStaffIdList()`](https://open.dingtalk.com/document/isvapp/obtain-the-list-of-employees-who-have-left) * [`internal.queryDocumentStatisticalData()`](https://open.dingtalk.com/document/orgapp/get-enterprise-document-statistics) * [`internal.queryDriveStatisticalData()`](https://open.dingtalk.com/document/orgapp/obtains-the-statistics-on-enterprise-dingtalk-trays) * [`internal.queryEmployeeTypeStatisticalData()`](https://open.dingtalk.com/document/orgapp/obtains-statistics-on-employee-types) * [`internal.queryGeneralDataService()`](https://open.dingtalk.com/document/orgapp-server/data-assets-platform-data-services-apis) * [`internal.queryGlobalInfo()`](https://open.dingtalk.com/document/isvapp/get-customer-management-global-information) * [`internal.queryGroupLiveStatisticalData()`](https://open.dingtalk.com/document/orgapp/obtains-the-live-stream-statistics-for-an-enterprise-group) * [`internal.queryGroupMember()`](https://open.dingtalk.com/document/isvapp/query-the-group-member-list) * [`internal.queryGroupMessageStatisticalData()`](https://open.dingtalk.com/document/orgapp/obtain-enterprise-group-chat-statistics) * [`internal.queryGroupMuteStatus()`](https://open.dingtalk.com/document/orgapp/query-group-silence-status) * [`internal.queryHealthStatisticalData()`](https://open.dingtalk.com/document/orgapp/queries-dingtalk-movement-statistics) * [`internal.queryHrmEmployeeDismissionInfo()`](https://open.dingtalk.com/document/isvapp/obtain-multiple-employee-demission-information-1) * [`internal.queryJobRanks()`](https://open.dingtalk.com/document/isvapp/obtain-a-list-of-enterprise-ranks) * [`internal.queryJobs()`](https://open.dingtalk.com/document/isvapp/obtains-a-list-of-enterprise-jobs) * [`internal.queryLiveInfo()`](https://open.dingtalk.com/document/isvapp/queries-the-live-streaming-information) * [`internal.queryLiveWatchDetail()`](https://open.dingtalk.com/document/orgapp/queries-the-playback-data-of-a-live-stream) * [`internal.queryLiveWatchUserList()`](https://open.dingtalk.com/document/isvapp/queries-the-viewing-information-of-viewers) * [`internal.queryMailStatisticalData()`](https://open.dingtalk.com/document/orgapp/queries-enterprise-email-statistics) * [`internal.queryMeetingRoom()`](https://open.dingtalk.com/document/isvapp/check-meeting-room-details) * [`internal.queryMeetingRoomDevice()`](https://open.dingtalk.com/document/app/querying-video-conference-device-information) * [`internal.queryMeetingRoomGroup()`](https://open.dingtalk.com/document/isvapp/query-meeting-room-group-information) * [`internal.queryMeetingRoomGroupList()`](https://open.dingtalk.com/document/isvapp/query-meeting-room-groups) * [`internal.queryMeetingRoomList()`](https://open.dingtalk.com/document/isvapp/check-the-meeting-room-list) * [`internal.queryOnlineUserStatisticalData()`](https://open.dingtalk.com/document/orgapp/retrieve-online-statistics-of-enterprise-users) * [`internal.queryOrgTodoTasks()`](https://open.dingtalk.com/document/isvapp/query-the-to-do-list-of-enterprise-users) * [`internal.queryPartnerInfo()`](https://open.dingtalk.com/document/isvapp/you-can-call-this-operation-to-retrieve-the-user-tag) * [`internal.queryPositions()`](https://open.dingtalk.com/document/isvapp/obtain-a-list-of-enterprise-positions) * [`internal.queryProcessesInstance()`](https://open.dingtalk.com/document/isvapp/query-flow-instances) * [`internal.queryProcessesWorkItems()`](https://open.dingtalk.com/document/isvapp/query-flow-instance-node-work-items) * [`internal.queryProject()`](https://open.dingtalk.com/document/orgapp/query-enterprise-all-projects) * [`internal.queryRedEnvelopeReciveStatisticalData()`](https://open.dingtalk.com/document/orgapp/queries-the-red-envelope-receiving-statistics-of-an-enterprise) * [`internal.queryRedEnvelopeSendStatisticalData()`](https://open.dingtalk.com/document/orgapp/obtains-the-statistics-on-red-packets-issued-by-enterprises) * [`internal.queryRelationDatasByTargetId()`](https://open.dingtalk.com/document/isvapp/querying-customer-data) * [`internal.queryReportStatisticalData()`](https://open.dingtalk.com/document/orgapp/obtain-enterprise-log-statistics) * [`internal.queryRobotPlugin()`](https://open.dingtalk.com/document/orgapp/quick-entrance-of-inquiry-single-chat-robot) * [`internal.querySceneGroupTemplateRobot()`](https://open.dingtalk.com/document/isvapp/query-intra-group-template-robot) * [`internal.queryScheduleConference()`](https://open.dingtalk.com/document/app/query-meeting-reservation) * [`internal.queryScheduleConferenceInfo()`](https://open.dingtalk.com/document/orgapp/query-appointment-meeting-history-meeting-information) * [`internal.queryServiceRecord()`](https://open.dingtalk.com/document/isvapp/execution-records-of-form-service-calls) * [`internal.querySingleGroup()`](https://open.dingtalk.com/document/isvapp/batch-query-cross-nail-two-group-list) * [`internal.querySingleMessageStatisticalData()`](https://open.dingtalk.com/document/orgapp/queries-the-statistics-on-one-time-enterprise-chats) * [`internal.queryTaskOfProject()`](https://open.dingtalk.com/document/isvapp/query-tasks-in-a-project) * [`internal.queryTelMeetingStatisticalData()`](https://open.dingtalk.com/document/orgapp/get-enterprise-teleconference-statistics) * [`internal.queryTodoStatisticalData()`](https://open.dingtalk.com/document/orgapp/obtains-the-to-do-statistics-of-an-enterprise) * [`internal.queryUnReadMessage()`](https://open.dingtalk.com/document/isvapp/query-the-number-of-unread-messages-for-users-outside-of) * [`internal.queryUnionOrder()`](https://open.dingtalk.com/document/isvapp/link-no-to-query-the-list-of-related-order-information) * [`internal.queryUserBehavior()`](https://open.dingtalk.com/document/app/obtain-anti-screen-capture-operation-records) * [`internal.queryVedioMeetingStatisticalData()`](https://open.dingtalk.com/document/orgapp/get-enterprise-video-conference-statistics) * [`internal.redirectTask()`](https://open.dingtalk.com/document/app/transfer-tasks) * [`internal.refundCommodity()`](https://open.dingtalk.com/document/isvapp-server/refund-of-goods) * [`internal.registerAndActivateDevice()`](https://open.dingtalk.com/document/isvapp/register-the-device-to-the-dingtalk) * [`internal.registerAndActivateDeviceBatch()`](https://open.dingtalk.com/document/isvapp/register-and-activate-devices-in-batches) * [`internal.registerCallback()`](https://open.dingtalk.com/document/orgapp/register-card-callback-address) * [`internal.releaseCommodity()`](https://open.dingtalk.com/document/isvapp-server/release-products) * [`internal.removeAttendee()`](https://open.dingtalk.com/document/isvapp/delete-schedule-participant) * [`internal.removeGroupMember()`](https://open.dingtalk.com/document/isvapp/remove-a-connected-group-member) * [`internal.removeMeetingRooms()`](https://open.dingtalk.com/document/isvapp/cancel-a-meeting-room-reservation) * [`internal.renameDentry()`](https://open.dingtalk.com/document/isvapp/rename-a-file-or-folder) * [`internal.respondEvent()`](https://open.dingtalk.com/document/isvapp/participants-respond-to-schedule-invitations) * [`internal.restoreRecycleItem()`](https://open.dingtalk.com/document/isvapp/restore-recycle-items) * [`internal.restoreRecycleItems()`](https://open.dingtalk.com/document/isvapp/batch-restore-recycled-items) * [`internal.revertDentryVersion()`](https://open.dingtalk.com/document/isvapp/restore-previous-versions-of-files) * [`internal.robotMessageFileDownload()`](https://open.dingtalk.com/document/isvapp/download-the-file-content-of-the-robot-receiving-message) * [`internal.rollbackInnerAppVersion()`](https://open.dingtalk.com/document/app/rollback-of-enterprise-internal-applet-version) * [`internal.rosterMetaFieldOptionsUpdate()`](https://open.dingtalk.com/document/orgapp/intelligent-personnel-roster-field-option-modification) * [`internal.saveBadgeCodeCorpInstance()`](https://open.dingtalk.com/document/isvapp/save-dingtalk-enterprise-instance) * [`internal.saveFormData()`](https://open.dingtalk.com/document/app/save-form-data) * [`internal.saveFormRemark()`](https://open.dingtalk.com/document/app/submit-comment) * [`internal.seachTaskStage()`](https://open.dingtalk.com/document/orgapp/get-task-list) * [`internal.searchActivationCode()`](https://open.dingtalk.com/document/isvapp-server/query-activation-code) * [`internal.searchDentries()`](https://open.dingtalk.com/document/orgapp/search-for-files) * [`internal.searchEmployeeFieldValues()`](https://open.dingtalk.com/document/app/gets-the-value-of-the-employee-component) * [`internal.searchFormDataIdList()`](https://open.dingtalk.com/document/app/obtain-the-ids-of-multiple-form-instances) * [`internal.searchFormDataSecondGeneration()`](https://open.dingtalk.com/document/isvapp/query-form-instances-using-advanced-search-conditions) * [`internal.searchFormDataSecondGenerationNoTableField()`](https://open.dingtalk.com/document/isvapp/obtain-form-instance-data-using-advanced-query-conditions-excluding-subform) * [`internal.searchFormDatas()`](https://open.dingtalk.com/document/app/querying-form-instance-data) * [`internal.searchProjectTemplate()`](https://open.dingtalk.com/document/isvapp/search-for-enterprise-custom-templates-by-project-template-name) * [`internal.searchTaskFlow()`](https://open.dingtalk.com/document/orgapp/query-task-workflow) * [`internal.searchTaskList()`](https://open.dingtalk.com/document/orgapp/query-task-grouping) * [`internal.searchTaskflowStatus()`](https://open.dingtalk.com/document/isvapp/search-task-workflow-status) * [`internal.searchUserTask()`](https://open.dingtalk.com/document/orgapp/querying-user-tasks) * [`internal.searchWorkspaces()`](https://open.dingtalk.com/document/orgapp/search-knowledge-base) * [`internal.send()`](https://open.dingtalk.com/document/orgapp/send-file-to-specified-session) * [`internal.sendAppDing()`](https://open.dingtalk.com/document/orgapp/send-in-application-ding) * [`internal.sendByApp()`](https://open.dingtalk.com/document/isvapp/sends-a-storage-file-to-a-specified-user) * [`internal.sendDingMessage()`](https://open.dingtalk.com/document/isvapp/dingtalk-users-send-messages-to-the-group-or-dingtalk-users) * [`internal.sendInteractiveCard()`](https://open.dingtalk.com/document/orgapp/send-interactive-dynamic-cards-1) * [`internal.sendLink()`](https://open.dingtalk.com/document/orgapp/send-a-file-link-to-the-specified-session) * [`internal.sendMessage()`](https://open.dingtalk.com/document/isvapp/a-user-outside-the-dingtalk-sends-a-message-to-the) * [`internal.sendMsgByTask()`](https://open.dingtalk.com/document/orgapp/service-group-sending-task-interface) * [`internal.sendOTOInteractiveCard()`](https://open.dingtalk.com/document/orgapp/send-dingtalk-interactive-cards-to-person-to-person-chat-sessions) * [`internal.sendOfficialAccountOTOMessage()`](https://open.dingtalk.com/document/isvapp/sends-a-single-message-from-the-service-window) * [`internal.sendPhoneDing()`](https://open.dingtalk.com/document/orgapp/outgoing-phone-ding) * [`internal.sendRobotInteractiveCard()`](https://open.dingtalk.com/document/orgapp/robots-send-interactive-cards) * [`internal.sendRobotMessage()`](https://open.dingtalk.com/document/app/group-robots-send-messages) * [`internal.sendServiceGroupMessage()`](https://open.dingtalk.com/document/orgapp/service-group-message-sending-interface) * [`internal.sendTemplateInteractiveCard()`](https://open.dingtalk.com/document/orgapp/send-lightweight-interactive-cards) * [`internal.serviceGroupCreateGroup()`](https://open.dingtalk.com/document/orgapp/create-a-scenario-service-group) * [`internal.serviceGroupUpdateGroupSet()`](https://open.dingtalk.com/document/isvapp/modify-a-service-group) * [`internal.setColumnsVisibility()`](https://open.dingtalk.com/document/app/set-column-visibility) * [`internal.setDeptPartnerTypeAndNum()`](https://open.dingtalk.com/document/isvapp/set-department-partner-type-and-partner-code) * [`internal.setMicroAppScope()`](https://open.dingtalk.com/document/orgapp/update-the-visible-range-of-micro-applications) * [`internal.setPermissionInheritance()`](https://open.dingtalk.com/document/orgapp/set-permission-inheritance-mode) * [`internal.setRobotPlugin()`](https://open.dingtalk.com/document/orgapp/set-robot-quick-entrance) * [`internal.setRowsVisibility()`](https://open.dingtalk.com/document/app/set-row-visibility) * [`internal.signIn()`](https://open.dingtalk.com/document/orgapp-server/sign-in-for-a-single-schedule) * [`internal.signOut()`](https://open.dingtalk.com/document/isvapp/sign-off-for-a-single-schedule) * [`internal.startCloudRecord()`](https://open.dingtalk.com/document/isvapp/video-conference-open-cloud-recording) * [`internal.startInstance()`](https://open.dingtalk.com/document/app/initiate-the-approval-process) * [`internal.startStreamOut()`](https://open.dingtalk.com/document/orgapp/video-conference-enables-live-stream-ingest) * [`internal.stopCloudRecord()`](https://open.dingtalk.com/document/isvapp/video-conferencing-stops-cloud-recording) * [`internal.stopStreamOut()`](https://open.dingtalk.com/document/orgapp/videoconferencing-stops-live-stream-ingest) * [`internal.storageAddPermission()`](https://open.dingtalk.com/document/isvapp/add-permissions-storage) * [`internal.storageAddPermission()`](https://open.dingtalk.com/document/orgapp/add-permissions-file) * [`internal.storageAddSpace()`](https://open.dingtalk.com/document/isvapp/add-space) * [`internal.storageCommitFile()`](https://open.dingtalk.com/document/app/submittal-file) * [`internal.storageCommitFile()`](https://open.dingtalk.com/document/orgapp/submit-documents) * [`internal.storageDeletePermission()`](https://open.dingtalk.com/document/isvapp/delete-storage-permissions) * [`internal.storageDeletePermission()`](https://open.dingtalk.com/document/orgapp/delete-permissions-file) * [`internal.storageGetFileUploadInfo()`](https://open.dingtalk.com/document/app/obtain-file-upload-informations) * [`internal.storageGetFileUploadInfo()`](https://open.dingtalk.com/document/orgapp/obtain-storage-upload-information) * [`internal.storageGetSpace()`](https://open.dingtalk.com/document/isvapp/get-space-information) * [`internal.storageUpdatePermission()`](https://open.dingtalk.com/document/isvapp/modify-storage-permissions) * [`internal.storageUpdatePermission()`](https://open.dingtalk.com/document/orgapp/modify-permissions-file) * [`internal.subscribeCalendar()`](https://open.dingtalk.com/document/orgapp/subscribe-to-a-public-calendar) * [`internal.suspendProject()`](https://open.dingtalk.com/document/orgapp/archiving-project) * [`internal.syncData()`](https://open.dingtalk.com/document/connector/dingtalk-connector-data-synchronization-interface) * [`internal.syncExceedApply()`](https://open.dingtalk.com/document/isvapp/dingtalk-oapi-alitrip-btrip-exceedapply-sync) * [`internal.terminateInstance()`](https://open.dingtalk.com/document/app/terminate-a-process-instance) * [`internal.unSuspendProject()`](https://open.dingtalk.com/document/orgapp/cancel-project-archiving) * [`internal.unsubscribeCalendar()`](https://open.dingtalk.com/document/orgapp/unsubscribe-from-a-public-calendar) * [`internal.unsubscribeEvent()`](https://open.dingtalk.com/document/isvapp/unsubscribe-from-file-change-event) * [`internal.updateBadgeCodeUserInstance()`](https://open.dingtalk.com/document/isvapp/update-dingtalk-user-instance) * [`internal.updateBizObject()`](https://open.dingtalk.com/document/isvapp/modify-form-business-object-data) * [`internal.updateCard()`](https://open.dingtalk.com/document/orgapp/interactive-card-update-interface) * [`internal.updateCrmPersonalCustomer()`](https://open.dingtalk.com/document/isvapp/update-crm-personal-customers) * [`internal.updateCustomfieldValue()`](https://open.dingtalk.com/document/isvapp/update-task-custom-field-value) * [`internal.updateDentryAppProperties()`](https://open.dingtalk.com/document/isvapp/update-the-application-properties-of-a-file-or-folder) * [`internal.updateFormData()`](https://open.dingtalk.com/document/orgapp/update-form-data) * [`internal.updateGroupAvatar()`](https://open.dingtalk.com/document/isvapp/modify-the-avatar-of-a-communication-group) * [`internal.updateGroupName()`](https://open.dingtalk.com/document/isvapp/modify-the-group-name) * [`internal.updateGroupSubAdmin()`](https://open.dingtalk.com/document/isvapp/update-group-administrator) * [`internal.updateInnerApp()`](https://open.dingtalk.com/document/app/update-internal-h5-applications) * [`internal.updateInstance()`](https://open.dingtalk.com/document/isvapp-server/update-process-instance-1) * [`internal.updateInteractiveCard()`](https://open.dingtalk.com/document/orgapp/update-dingtalk-interactive-cards-1) * [`internal.updateLive()`](https://open.dingtalk.com/document/isvapp/modify-live-streaming) * [`internal.updateMeetingRoom()`](https://open.dingtalk.com/document/isvapp/update-meeting-room-information) * [`internal.updateMeetingRoomGroup()`](https://open.dingtalk.com/document/isvapp/update-meeting-room-group-information) * [`internal.updateMemberBanWords()`](https://open.dingtalk.com/document/orgapp/set-group-members-access-control) * [`internal.updateMemberGroupNick()`](https://open.dingtalk.com/document/isvapp/update-group-nicknames-for-group-members) * [`internal.updateOrganizationTaskContent()`](https://open.dingtalk.com/document/isvapp/update-free-task-title) * [`internal.updateOrganizationTaskDueDate()`](https://open.dingtalk.com/document/isvapp/update-free-task-deadline) * [`internal.updateOrganizationTaskExecutor()`](https://open.dingtalk.com/document/isvapp/update-free-task-executor) * [`internal.updateOrganizationTaskInvolveMembers()`](https://open.dingtalk.com/document/isvapp/add-or-remove-participants-of-free-tasks) * [`internal.updateOrganizationTaskNote()`](https://open.dingtalk.com/document/isvapp/update-free-task-notes-isv) * [`internal.updateOrganizationTaskPriority()`](https://open.dingtalk.com/document/isvapp/update-the-priority-of-a-free-migration-job) * [`internal.updateOrganizationTaskStatus()`](https://open.dingtalk.com/document/isvapp/update-free-task-status) * [`internal.updateProjectGroup()`](https://open.dingtalk.com/document/isvapp/update-project-grouping) * [`internal.updateRobotInteractiveCard()`](https://open.dingtalk.com/document/orgapp/update-the-robot-to-send-interactive-cards) * [`internal.updateScheduleConference()`](https://open.dingtalk.com/document/app/update-appointment-meeting) * [`internal.updateStatus()`](https://open.dingtalk.com/document/isvapp-server/update-status) * [`internal.updateTaskContent()`](https://open.dingtalk.com/document/orgapp/update-task-content) * [`internal.updateTaskDueDate()`](https://open.dingtalk.com/document/orgapp/update-task-deadline) * [`internal.updateTaskExecutor()`](https://open.dingtalk.com/document/orgapp/update-task-performer) * [`internal.updateTaskInvolvemembers()`](https://open.dingtalk.com/document/orgapp/update-task-participants) * [`internal.updateTaskNote()`](https://open.dingtalk.com/document/orgapp/update-task-notes) * [`internal.updateTaskPriority()`](https://open.dingtalk.com/document/orgapp/update-task-priority) * [`internal.updateTaskStartdate()`](https://open.dingtalk.com/document/orgapp/update-task-start-time) * [`internal.updateTaskTaskflowstatus()`](https://open.dingtalk.com/document/isvapp/update-task-workflow-status) * [`internal.updateTodoTask()`](https://open.dingtalk.com/document/isvapp/updates-dingtalk-to-do-tasks) * [`internal.updateTodoTaskExecutorStatus()`](https://open.dingtalk.com/document/isvapp/update-dingtalk-to-do-status) * [`internal.upgradeCloudGroup()`](https://open.dingtalk.com/document/orgapp/upgraded-the-cloud-customer-service-group-to-the-dingtalk-intelligent) * [`internal.upgradeDevice()`](https://open.dingtalk.com/document/app/upgrade-equipment) * [`internal.upgradeNormalGroup()`](https://open.dingtalk.com/document/orgapp/a-dingtalk-group-is-upgraded-to-one-of-the-intelligent) * [`internal.validateOrderBuy()`](https://open.dingtalk.com/document/isvapp-server/multi-channel-new-purchase-verification) * [`internal.validateOrderUpgrade()`](https://open.dingtalk.com/document/isvapp-server/verification-order-upgrade) * [`internal.wikiWordsDetail()`](https://open.dingtalk.com/document/orgapp/enterprise-encyclopedia-query-entry-details-by-entry-name) * [`internal.wikiWordsParse()`](https://open.dingtalk.com/document/orgapp/enterprise-encyclopedia-match-entries-in-a-text) --- --- url: /plugins/adapter/discord.md --- # @koishijs/plugin-adapter-discord ## 接入方法 1. 前往 ,登录账号创建一个应用 2. 点击「Bot」并创建一个新的机器人,保存这个页面中的 token(请注意不要泄露) 3. 要将机器人拉进你的服务器,点击「OAuth2」,并在网址生成器中勾选 Bot 和机器人所需要的权限 4. 打开生成的链接,选择你具有管理权限的服务器,就成功把机器人添加进去了 5. 将上面的 token 作为机器人配置项即可使用 ## 配置项 包括全部的 [`WsClient`](../../api/core/adapter.md#类-adapter-wsclient) 选项和下列额外选项: ### options.token * 类型: `string` 机器人账户的令牌。 ### options.endpoint * 类型: `string` * 默认值: `'https://discord.com/api/v8'` 要连接的服务器地址。 ### options.proxyAgent * 类型: `string` * 默认值: [`app.config.request.proxyAgent`](../../api/core/app.md#options-request-proxyagent) 请求时默认使用的网络代理。 ### options.gateway * 类型: `string` * 默认值: `'wss://gateway.discord.gg/?v=8&encoding=json'` 要连接的 WebSocket 网关。 ### options.intents * 类型: `number` * 默认值: `GUILD_MESSAGES | GUILD_MESSAGE_REACTIONS | DIRECT_MESSAGES | DIRECT_MESSAGE_REACTIONS` 需要订阅的机器人事件。这是一个以 bitset 形式表达的正整数,每一位代表一类事件。 参考:[Gateway Intents](https://discord.com/developers/docs/topics/gateway#gateway-intents) ### options.handleExternalAsset * 可选值: `string` * 默认值: `'auto'` 指定单独发送外链资源时采用的方法: * **download:** 先下载后发送 * **direct:** 直接发送链接 * **auto:** 发送一个 HEAD 请求,如果返回的 Content-Type 正确,则直接发送链接,否则先下载后发送 ### options.handleMixedContent * 可选值: `string` * 默认值: `'auto'` 指定发送图文混合内容时采用的方法: * **separate:** 将每个不同形式的内容分开发送 * **attach:** 图片前如果有文本内容,则将文本作为图片的附带信息进行发送 * **auto:** 如果图片本身采用直接发送则与前面的文本分开,否则将文本作为图片的附带信息发送 当配置为 `attach` 并且发送文本+图片形式的消息时,无论 [`handleExternalAsset`](#options-discord-handleexternalasset) 配置为何都会先下载后发送。 ## 内部接口 ::: tip 关于内部接口的使用方式,请参见 [访问内部接口](../../guide/adapter/bot.md#access-from-plugin)。 ::: * [`internal.addGuildMember()`](https://discord.com/developers/docs/resources/guild#add-guild-member) * [`internal.addGuildMemberRole()`](https://discord.com/developers/docs/resources/guild#add-guild-member-role) * [`internal.addThreadMember()`](https://discord.com/developers/docs/resources/channel#add-thread-member) * [`internal.beginGuildPrune()`](https://discord.com/developers/docs/resources/guild#begin-guild-prune) * [`internal.bulkDeleteMessages()`](https://discord.com/developers/docs/resources/channel#bulk-delete-messages) * [`internal.bulkOverwriteGlobalApplicationCommands()`](https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands) * [`internal.bulkOverwriteGuildApplicationCommands()`](https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-guild-application-commands) * [`internal.createAutoModerationRule()`](https://discord.com/developers/docs/resources/auto-moderation#create-auto-moderation-rule) * [`internal.createChannelInvite()`](https://discord.com/developers/docs/resources/channel#create-channel-invite) * [`internal.createDM()`](https://discord.com/developers/docs/resources/user#create-dm) * [`internal.createFollowupMessage()`](https://discord.com/developers/docs/interactions/receiving-and-responding#create-followup-message) * [`internal.createGlobalApplicationCommand()`](https://discord.com/developers/docs/interactions/application-commands#create-global-application-command) * [`internal.createGroupDM()`](https://discord.com/developers/docs/resources/user#create-group-dm) * [`internal.createGuild()`](https://discord.com/developers/docs/resources/guild#create-guild) * [`internal.createGuildApplicationCommand()`](https://discord.com/developers/docs/interactions/application-commands#create-guild-application-command) * [`internal.createGuildBan()`](https://discord.com/developers/docs/resources/guild#create-guild-ban) * [`internal.createGuildChannel()`](https://discord.com/developers/docs/resources/guild#create-guild-channel) * [`internal.createGuildEmoji()`](https://discord.com/developers/docs/resources/emoji#create-guild-emoji) * [`internal.createGuildRole()`](https://discord.com/developers/docs/resources/guild#create-guild-role) * [`internal.createGuildScheduledEvent()`](https://discord.com/developers/docs/resources/guild-scheduled-event#create-guild-scheduled-event) * [`internal.createGuildSticker()`](https://discord.com/developers/docs/resources/sticker#create-guild-sticker) * [`internal.createGuildTemplate()`](https://discord.com/developers/docs/resources/guild-template#create-guild-template) * [`internal.createGuildfromGuildTemplate()`](https://discord.com/developers/docs/resources/guild-template#create-guild-from-guild-template) * [`internal.createInteractionResponse()`](https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response) * [`internal.createMessage()`](https://discord.com/developers/docs/resources/channel#create-message) * [`internal.createReaction()`](https://discord.com/developers/docs/resources/channel#create-reaction) * [`internal.createStageInstance()`](https://discord.com/developers/docs/resources/stage-instance#create-stage-instance) * [`internal.createWebhook()`](https://discord.com/developers/docs/resources/webhook#create-webhook) * [`internal.crosspostMessage()`](https://discord.com/developers/docs/resources/channel#crosspost-message) * [`internal.deleteAllReactions()`](https://discord.com/developers/docs/resources/channel#delete-all-reactions) * [`internal.deleteAllReactionsForEmoji()`](https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji) * [`internal.deleteAutoModerationRule()`](https://discord.com/developers/docs/resources/auto-moderation#delete-auto-moderation-rule) * [`internal.deleteChannel()`](https://discord.com/developers/docs/resources/channel#deleteclose-channel) * [`internal.deleteChannelPermission()`](https://discord.com/developers/docs/resources/channel#delete-channel-permission) * [`internal.deleteFollowupMessage()`](https://discord.com/developers/docs/interactions/receiving-and-responding#delete-followup-message) * [`internal.deleteGlobalApplicationCommand()`](https://discord.com/developers/docs/interactions/application-commands#delete-global-application-command) * [`internal.deleteGuild()`](https://discord.com/developers/docs/resources/guild#delete-guild) * [`internal.deleteGuildApplicationCommand()`](https://discord.com/developers/docs/interactions/application-commands#delete-guild-application-command) * [`internal.deleteGuildIntegration()`](https://discord.com/developers/docs/resources/guild#delete-guild-integration) * [`internal.deleteGuildEmoji()`](https://discord.com/developers/docs/resources/emoji#delete-guild-emoji) * [`internal.deleteGuildRole()`](https://discord.com/developers/docs/resources/guild#delete-guild-role) * [`internal.deleteGuildScheduledEvent()`](https://discord.com/developers/docs/resources/guild-scheduled-event#delete-guild-scheduled-event) * [`internal.deleteGuildSticker()`](https://discord.com/developers/docs/resources/sticker#delete-guild-sticker) * [`internal.deleteGuildTemplate()`](https://discord.com/developers/docs/resources/guild-template#delete-guild-template) * [`internal.deleteInvite()`](https://discord.com/developers/docs/resources/invite#delete-invite) * [`internal.deleteMessage()`](https://discord.com/developers/docs/resources/channel#delete-message) * [`internal.deleteOriginalInteractionResponse()`](https://discord.com/developers/docs/interactions/receiving-and-responding#delete-original-interaction-response) * [`internal.deleteOwnReaction()`](https://discord.com/developers/docs/resources/channel#delete-own-reaction) * [`internal.deleteStageInstance()`](https://discord.com/developers/docs/resources/stage-instance#delete-stage-instance) * [`internal.deleteUserReaction()`](https://discord.com/developers/docs/resources/channel#delete-user-reaction) * [`internal.deleteWebhook()`](https://discord.com/developers/docs/resources/webhook#delete-webhook) * [`internal.deleteWebhookMessage()`](https://discord.com/developers/docs/resources/webhook#delete-webhook-message) * [`internal.deleteWebhookwithToken()`](https://discord.com/developers/docs/resources/webhook#delete-webhook-with-token) * [`internal.editApplicationCommandPermissions()`](https://discord.com/developers/docs/interactions/application-commands#edit-application-command-permissions) * [`internal.editChannelPermissions()`](https://discord.com/developers/docs/resources/channel#edit-channel-permissions) * [`internal.editFollowupMessage()`](https://discord.com/developers/docs/interactions/receiving-and-responding#edit-followup-message) * [`internal.editGlobalApplicationCommand()`](https://discord.com/developers/docs/interactions/application-commands#edit-global-application-command) * [`internal.editGuildApplicationCommand()`](https://discord.com/developers/docs/interactions/application-commands#edit-guild-application-command) * [`internal.editMessage()`](https://discord.com/developers/docs/resources/channel#edit-message) * [`internal.editOriginalInteractionResponse()`](https://discord.com/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response) * [`internal.editWebhookMessage()`](https://discord.com/developers/docs/resources/webhook#edit-webhook-message) * [`internal.executeGitHubCompatibleWebhook()`](https://discord.com/developers/docs/resources/webhook#execute-githubcompatible-webhook) * [`internal.executeSlackCompatibleWebhook()`](https://discord.com/developers/docs/resources/webhook#execute-slackcompatible-webhook) * [`internal.executeWebhook()`](https://discord.com/developers/docs/resources/webhook#execute-webhook) * [`internal.followNewsChannel()`](https://discord.com/developers/docs/resources/channel#follow-news-channel) * [`internal.getApplicationCommandPermissions()`](https://discord.com/developers/docs/interactions/application-commands#get-application-command-permissions) * [`internal.getApplicationRoleConnectionMetadataRecords()`](https://discord.com/developers/docs/resources/application-role-connection-metadata#get-application-role-connection-metadata-records) * [`internal.getAutoModerationRule()`](https://discord.com/developers/docs/resources/auto-moderation#get-auto-moderation-rule) * [`internal.getChannel()`](https://discord.com/developers/docs/resources/channel#get-channel) * [`internal.getChannelInvites()`](https://discord.com/developers/docs/resources/channel#get-channel-invites) * [`internal.getChannelMessage()`](https://discord.com/developers/docs/resources/channel#get-channel-message) * [`internal.getChannelMessages()`](https://discord.com/developers/docs/resources/channel#get-channel-messages) * [`internal.getChannelWebhooks()`](https://discord.com/developers/docs/resources/webhook#get-channel-webhooks) * [`internal.getCurrentAuthorizationInformation()`](https://discord.com/developers/docs/topics/oauth2#get-current-authorization-information) * [`internal.getCurrentBotApplicationInformation()`](https://discord.com/developers/docs/topics/oauth2#get-current-bot-application-information) * [`internal.getCurrentUser()`](https://discord.com/developers/docs/resources/user#get-current-user) * [`internal.getCurrentUserGuildMember()`](https://discord.com/developers/docs/resources/user#get-current-user-guild-member) * [`internal.getCurrentUserGuilds()`](https://discord.com/developers/docs/resources/user#get-current-user-guilds) * [`internal.getFollowupMessage()`](https://discord.com/developers/docs/interactions/receiving-and-responding#get-followup-message) * [`internal.getGateway()`](https://discord.com/developers/docs/topics/gateway#get-gateway) * [`internal.getGatewayBot()`](https://discord.com/developers/docs/topics/gateway#get-gateway-bot) * [`internal.getGlobalApplicationCommand()`](https://discord.com/developers/docs/interactions/application-commands#get-global-application-command) * [`internal.getGlobalApplicationCommands()`](https://discord.com/developers/docs/interactions/application-commands#get-global-application-commands) * [`internal.getGuild()`](https://discord.com/developers/docs/resources/guild#get-guild) * [`internal.getGuildApplicationCommand()`](https://discord.com/developers/docs/interactions/application-commands#get-guild-application-command) * [`internal.getGuildApplicationCommandPermissions()`](https://discord.com/developers/docs/interactions/application-commands#get-guild-application-command-permissions) * [`internal.getGuildApplicationCommands()`](https://discord.com/developers/docs/interactions/application-commands#get-guild-application-commands) * [`internal.getGuildAuditLog()`](https://discord.com/developers/docs/resources/audit-log#get-guild-audit-log) * [`internal.getGuildBan()`](https://discord.com/developers/docs/resources/guild#get-guild-ban) * [`internal.getGuildBans()`](https://discord.com/developers/docs/resources/guild#get-guild-bans) * [`internal.getGuildChannels()`](https://discord.com/developers/docs/resources/guild#get-guild-channels) * [`internal.getGuildEmoji()`](https://discord.com/developers/docs/resources/emoji#get-guild-emoji) * [`internal.getGuildIntegrations()`](https://discord.com/developers/docs/resources/guild#get-guild-integrations) * [`internal.getGuildInvites()`](https://discord.com/developers/docs/resources/guild#get-guild-invites) * [`internal.getGuildMember()`](https://discord.com/developers/docs/resources/guild#get-guild-member) * [`internal.getGuildPreview()`](https://discord.com/developers/docs/resources/guild#get-guild-preview) * [`internal.getGuildPruneCount()`](https://discord.com/developers/docs/resources/guild#get-guild-prune-count) * [`internal.getGuildRoles()`](https://discord.com/developers/docs/resources/guild#get-guild-roles) * [`internal.getGuildScheduledEvent()`](https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event) * [`internal.getGuildScheduledEventUsers()`](https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event-users) * [`internal.getGuildSticker()`](https://discord.com/developers/docs/resources/sticker#get-guild-sticker) * [`internal.getGuildTemplate()`](https://discord.com/developers/docs/resources/guild-template#get-guild-template) * [`internal.getGuildTemplates()`](https://discord.com/developers/docs/resources/guild-template#get-guild-templates) * [`internal.getGuildVanityURL()`](https://discord.com/developers/docs/resources/guild#get-guild-vanity-url) * [`internal.getGuildVoiceRegions()`](https://discord.com/developers/docs/resources/guild#get-guild-voice-regions) * [`internal.getGuildWebhooks()`](https://discord.com/developers/docs/resources/webhook#get-guild-webhooks) * [`internal.getGuildWelcomeScreen()`](https://discord.com/developers/docs/resources/guild#get-guild-welcome-screen) * [`internal.getGuildWidget()`](https://discord.com/developers/docs/resources/guild#get-guild-widget) * [`internal.getGuildWidgetImage()`](https://discord.com/developers/docs/resources/guild#get-guild-widget-image) * [`internal.getGuildWidgetSettings()`](https://discord.com/developers/docs/resources/guild#get-guild-widget-settings) * [`internal.getInvite()`](https://discord.com/developers/docs/resources/invite#get-invite) * [`internal.getOriginalInteractionResponse()`](https://discord.com/developers/docs/interactions/receiving-and-responding#get-original-interaction-response) * [`internal.getPinnedMessages()`](https://discord.com/developers/docs/resources/channel#get-pinned-messages) * [`internal.getReactions()`](https://discord.com/developers/docs/resources/channel#get-reactions) * [`internal.getStageInstance()`](https://discord.com/developers/docs/resources/stage-instance#get-stage-instance) * [`internal.getSticker()`](https://discord.com/developers/docs/resources/sticker#get-sticker) * [`internal.getThreadMember()`](https://discord.com/developers/docs/resources/channel#get-thread-member) * [`internal.getUser()`](https://discord.com/developers/docs/resources/user#get-user) * [`internal.getUserConnections()`](https://discord.com/developers/docs/resources/user#get-user-connections) * [`internal.getWebhook()`](https://discord.com/developers/docs/resources/webhook#get-webhook) * [`internal.getWebhookMessage()`](https://discord.com/developers/docs/resources/webhook#get-webhook-message) * [`internal.getWebhookWithToken()`](https://discord.com/developers/docs/resources/webhook#get-webhook-with-token) * [`internal.groupDMAddRecipient()`](https://discord.com/developers/docs/resources/channel#group-dm-add-recipient) * [`internal.groupDMRemoveRecipient()`](https://discord.com/developers/docs/resources/channel#group-dm-remove-recipient) * [`internal.joinThread()`](https://discord.com/developers/docs/resources/channel#join-thread) * [`internal.leaveGuild()`](https://discord.com/developers/docs/resources/user#leave-guild) * [`internal.leaveThread()`](https://discord.com/developers/docs/resources/channel#leave-thread) * [`internal.listActiveGuildThreads()`](https://discord.com/developers/docs/resources/guild#list-active-guild-threads) * [`internal.listActiveThreads()`](https://discord.com/developers/docs/resources/channel#list-active-threads) * [`internal.listAutoModerationRules()`](https://discord.com/developers/docs/resources/auto-moderation#list-auto-moderation-rules-for-guild) * [`internal.listGuildEmojis()`](https://discord.com/developers/docs/resources/emoji#list-guild-emojis) * [`internal.listGuildMembers()`](https://discord.com/developers/docs/resources/guild#list-guild-members) * [`internal.listGuildStickers()`](https://discord.com/developers/docs/resources/sticker#list-guild-stickers) * [`internal.listJoinedPrivateArchivedThreads()`](https://discord.com/developers/docs/resources/channel#list-joined-private-archived-threads) * [`internal.listNitroStickerPacks()`](https://discord.com/developers/docs/resources/sticker#list-nitro-sticker-packs) * [`internal.listPrivateArchivedThreads()`](https://discord.com/developers/docs/resources/channel#list-private-archived-threads) * [`internal.listPublicArchivedThreads()`](https://discord.com/developers/docs/resources/channel#list-public-archived-threads) * [`internal.listScheduledEventsforGuild()`](https://discord.com/developers/docs/resources/guild-scheduled-event#list-scheduled-events-for-guild) * [`internal.listThreadMembers()`](https://discord.com/developers/docs/resources/channel#list-thread-members) * [`internal.listVoiceRegions()`](https://discord.com/developers/docs/resources/voice#list-voice-regions) * [`internal.modifyAutoModerationRule()`](https://discord.com/developers/docs/resources/auto-moderation#modify-auto-moderation-rule) * [`internal.modifyChannel()`](https://discord.com/developers/docs/resources/channel#modify-channel) * [`internal.modifyCurrentMember()`](https://discord.com/developers/docs/resources/guild#modify-current-member) * [`internal.modifyCurrentUser()`](https://discord.com/developers/docs/resources/user#modify-current-user) * [`internal.modifyCurrentUserVoiceState()`](https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state) * [`internal.modifyGuild()`](https://discord.com/developers/docs/resources/guild#modify-guild) * [`internal.modifyGuildChannelPositions()`](https://discord.com/developers/docs/resources/guild#modify-guild-channel-positions) * [`internal.modifyGuildEmoji()`](https://discord.com/developers/docs/resources/emoji#modify-guild-emoji) * [`internal.modifyGuildMember()`](https://discord.com/developers/docs/resources/guild#modify-guild-member) * [`internal.modifyGuildRole()`](https://discord.com/developers/docs/resources/guild#modify-guild-role) * [`internal.modifyGuildRolePositions()`](https://discord.com/developers/docs/resources/guild#modify-guild-role-positions) * [`internal.modifyGuildScheduledEvent()`](https://discord.com/developers/docs/resources/guild-scheduled-event#modify-guild-scheduled-event) * [`internal.modifyGuildSticker()`](https://discord.com/developers/docs/resources/sticker#modify-guild-sticker) * [`internal.modifyGuildTemplate()`](https://discord.com/developers/docs/resources/guild-template#modify-guild-template) * [`internal.modifyGuildWelcomeScreen()`](https://discord.com/developers/docs/resources/guild#modify-guild-welcome-screen) * [`internal.modifyGuildWidget()`](https://discord.com/developers/docs/resources/guild#modify-guild-widget) * [`internal.modifyStageInstance()`](https://discord.com/developers/docs/resources/stage-instance#modify-stage-instance) * [`internal.modifyUserVoiceState()`](https://discord.com/developers/docs/resources/guild#modify-user-voice-state) * [`internal.modifyWebhook()`](https://discord.com/developers/docs/resources/webhook#modify-webhook) * [`internal.modifyWebhookWithToken()`](https://discord.com/developers/docs/resources/webhook#modify-webhook-with-token) * [`internal.pinMessage()`](https://discord.com/developers/docs/resources/channel#pin-message) * [`internal.removeGuildBan()`](https://discord.com/developers/docs/resources/guild#remove-guild-ban) * [`internal.removeGuildMember()`](https://discord.com/developers/docs/resources/guild#remove-guild-member) * [`internal.removeGuildMemberRole()`](https://discord.com/developers/docs/resources/guild#remove-guild-member-role) * [`internal.removeThreadMember()`](https://discord.com/developers/docs/resources/channel#remove-thread-member) * [`internal.searchGuildMembers()`](https://discord.com/developers/docs/resources/guild#search-guild-members) * [`internal.startThreadFromMessage()`](https://discord.com/developers/docs/resources/channel#start-thread-from-message) * [`internal.startThreadInForumChannel()`](https://discord.com/developers/docs/resources/channel#start-thread-in-forum-channel) * [`internal.startThreadWithoutMessage()`](https://discord.com/developers/docs/resources/channel#start-thread-without-message) * [`internal.syncGuildTemplate()`](https://discord.com/developers/docs/resources/guild-template#sync-guild-template) * [`internal.triggerTypingIndicator()`](https://discord.com/developers/docs/resources/channel#trigger-typing-indicator) * [`internal.unpinMessage()`](https://discord.com/developers/docs/resources/channel#unpin-message) * [`internal.updateApplicationRoleConnectionMetadataRecords()`](https://discord.com/developers/docs/resources/application-role-connection-metadata#update-application-role-connection-metadata-records) ## 内部事件 * [`discord/application-command-permissions-update`](https://discord.com/developers/docs/topics/gateway-events#application-command-permissions-update) * [`discord/auto-moderation-action-execution`](https://discord.com/developers/docs/topics/gateway-events#auto-moderation-action-execution) * [`discord/auto-moderation-rule-create`](https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-create) * [`discord/auto-moderation-rule-delete`](https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-delete) * [`discord/auto-moderation-rule-update`](https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-update) * [`discord/channel-create`](https://discord.com/developers/docs/topics/gateway-events#channel-create) * [`discord/channel-delete`](https://discord.com/developers/docs/topics/gateway-events#channel-delete) * [`discord/channel-pins-update`](https://discord.com/developers/docs/topics/gateway-events#channel-pins-update) * [`discord/channel-update`](https://discord.com/developers/docs/topics/gateway-events#channel-update) * [`discord/guild-audit-log-entry-create`](https://discord.com/developers/docs/topics/gateway-events#guild-audit-log-entry-create) * [`discord/guild-ban-add`](https://discord.com/developers/docs/topics/gateway-events#guild-ban-add) * [`discord/guild-ban-remove`](https://discord.com/developers/docs/topics/gateway-events#guild-ban-remove) * [`discord/guild-create`](https://discord.com/developers/docs/topics/gateway-events#guild-create) * [`discord/guild-delete`](https://discord.com/developers/docs/topics/gateway-events#guild-delete) * [`discord/guild-emojis-update`](https://discord.com/developers/docs/topics/gateway-events#guild-emojis-update) * [`discord/guild-integrations-update`](https://discord.com/developers/docs/topics/gateway-events#guild-integrations-update) * [`discord/guild-member-add`](https://discord.com/developers/docs/topics/gateway-events#guild-member-add) * [`discord/guild-member-remove`](https://discord.com/developers/docs/topics/gateway-events#guild-member-remove) * [`discord/guild-member-update`](https://discord.com/developers/docs/topics/gateway-events#guild-member-update) * [`discord/guild-members-chunk`](https://discord.com/developers/docs/topics/gateway-events#guild-members-chunk) * [`discord/guild-role-create`](https://discord.com/developers/docs/topics/gateway-events#guild-role-create) * [`discord/guild-role-delete`](https://discord.com/developers/docs/topics/gateway-events#guild-role-delete) * [`discord/guild-role-update`](https://discord.com/developers/docs/topics/gateway-events#guild-role-update) * [`discord/guild-scheduled-event-create`](https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-create) * [`discord/guild-scheduled-event-delete`](https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-delete) * [`discord/guild-scheduled-event-update`](https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-update) * [`discord/guild-scheduled-event-user-add`](https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-user-add) * [`discord/guild-scheduled-event-user-remove`](https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-user-remove) * [`discord/guild-stickers-update`](https://discord.com/developers/docs/topics/gateway-events#guild-stickers-update) * [`discord/guild-update`](https://discord.com/developers/docs/topics/gateway-events#guild-update) * [`discord/hello`](https://discord.com/developers/docs/topics/gateway-events#hello) * [`discord/integration-create`](https://discord.com/developers/docs/topics/gateway-events#integration-create) * [`discord/integration-delete`](https://discord.com/developers/docs/topics/gateway-events#integration-delete) * [`discord/integration-update`](https://discord.com/developers/docs/topics/gateway-events#integration-update) * [`discord/interaction-create`](https://discord.com/developers/docs/topics/gateway-events#interaction-create) * [`discord/invalid-session`](https://discord.com/developers/docs/topics/gateway-events#invalid-session) * [`discord/invite-create`](https://discord.com/developers/docs/topics/gateway-events#invite-create) * [`discord/invite-delete`](https://discord.com/developers/docs/topics/gateway-events#invite-delete) * [`discord/message-create`](https://discord.com/developers/docs/topics/gateway-events#message-create) * [`discord/message-delete-bulk`](https://discord.com/developers/docs/topics/gateway-events#message-delete-bulk) * [`discord/message-delete`](https://discord.com/developers/docs/topics/gateway-events#message-delete) * [`discord/message-reaction-add`](https://discord.com/developers/docs/topics/gateway-events#message-reaction-add) * [`discord/message-reaction-remove-all`](https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove-all) * [`discord/message-reaction-remove-emoji`](https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove-emoji) * [`discord/message-reaction-remove`](https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove) * [`discord/message-update`](https://discord.com/developers/docs/topics/gateway-events#message-update) * [`discord/presence-update`](https://discord.com/developers/docs/topics/gateway-events#presence-update) * [`discord/ready`](https://discord.com/developers/docs/topics/gateway-events#ready) * [`discord/reconnect`](https://discord.com/developers/docs/topics/gateway-events#reconnect) * [`discord/resumed`](https://discord.com/developers/docs/topics/gateway-events#resumed) * [`discord/stage-instance-create`](https://discord.com/developers/docs/topics/gateway-events#stage-instance-create) * [`discord/stage-instance-delete`](https://discord.com/developers/docs/topics/gateway-events#stage-instance-delete) * [`discord/stage-instance-update`](https://discord.com/developers/docs/topics/gateway-events#stage-instance-update) * [`discord/thread-create`](https://discord.com/developers/docs/topics/gateway-events#thread-create) * [`discord/thread-delete`](https://discord.com/developers/docs/topics/gateway-events#thread-delete) * [`discord/thread-list-sync`](https://discord.com/developers/docs/topics/gateway-events#thread-list-sync) * [`discord/thread-member-update`](https://discord.com/developers/docs/topics/gateway-events#thread-member-update) * [`discord/thread-members-update`](https://discord.com/developers/docs/topics/gateway-events#thread-members-update) * [`discord/thread-update`](https://discord.com/developers/docs/topics/gateway-events#thread-update) * [`discord/typing-start`](https://discord.com/developers/docs/topics/gateway-events#typing-start) * [`discord/user-update`](https://discord.com/developers/docs/topics/gateway-events#user-update) * [`discord/voice-server-update`](https://discord.com/developers/docs/topics/gateway-events#voice-server-update) * [`discord/voice-state-update`](https://discord.com/developers/docs/topics/gateway-events#voice-state-update) * [`discord/webhooks-update`](https://discord.com/developers/docs/topics/gateway-events#webhooks-update) --- --- url: /plugins/adapter/kook.md --- # @koishijs/plugin-adapter-kook ::: tip 如果选择通信方式为 `http`,那么你需要准备一个带有 SSL 证书的公网域名,并将 Koishi [部署到公网](../../manual/recipe/server.md)。 ::: ## 接入方法 1. 前往 [开发者平台](https://developer.kookapp.cn/),选择「机器人」并点击「新建」 2. 根据自身需要,在「机器人连接模式」中选择 Webhook 或 WebSocket 中的一种: * 如果是 Webhook,请记下页面中的 `token` 和 `verify_token` (请注意不要泄露),并作为机器人的配置项,同时让 Koishi 暴露一个 URL,填入下方的 Callback URL 中,启动 Koishi 后点击「机器人上线」 * 如果是 WebSocket,则只需记录 `token` (请注意不要泄露) 并作为机器人的配置项即可,你可以在任何时候启动 Koishi ## 基础配置项 ### options.protocol * 可选值: http, ws 要使用的协议类型。 ### options.token * 类型: `string` 机器人账户的令牌。 ### options.endpoint * 类型: `string` * 默认值: `'https://www.kookapp.cn/api/v3'` 要请求的 API 网址。 ### options.attachMode * 类型: `'separate' | 'mixed' | 'card'` * 默认值: `'separate'` 控制当尝试发送含有[资源元素](../../api/message/elements.md#资源元素)的消息时的行为。 * **separate:** 每一个资源消息段,以及资源消息段之间的文本都将单独发送一条消息 * **mixed:** 当要发送的内容中含有多个资源消息段或资源消息段和文本的混合时,发送卡片消息;否则将单独发送资源消息段 * **card:** 当要发送的内容中含有资源消息段,则以卡片消息的形式发送 ## HTTP 配置项 ### options.verifyToken * 类型: `string` 机器人账户的验证令牌。 ### options.path * 类型:`string` * 默认值:`'/kook'` 服务器监听的路径。 ## WS 配置项 包括全部的 [`WsClient`](../../api/core/adapter.md#类-adapter-wsclient) 选项。 --- --- url: /plugins/adapter/lark.md --- # @koishijs/plugin-adapter-lark ::: warning 在接入之前,你需要准备一个带有 SSL 证书的公网域名,并将 Koishi [部署到公网](../../manual/recipe/server.md)。 ::: ## 接入方法 1. 在开发者后台 ([Lark](https://open.larksuite.com/app/) / [飞书](https://open.feishu.cn/app/)) 新建**企业自建应用**,点击应用名称进入应用详情页。 2. 点击凭证与基础信息,获取 App ID 和 App Secret 值,填写到插件配置对应字段。 3. 点击事件订阅,获取 Encrypt Key 和 Verification Token 值,填写到插件配置对应字段。 4. 在事件订阅页面,确保添加了 `接收消息v2.0` (`im.message.receive_v1`) 事件。 5. 如果你的机器人需要发送图片消息,你需要确保添加了 `获取与上传图片或文件资源` (`im:resource`) 事件 6. 按实际情况配置插件或机器人全局的 `selfUrl` 值,然后启动插件。 7. 将第 6 步中配置的值加上 `path` 的值,填写到飞书开发者后台事件订阅页面的**请求地址配置**中,并点击完成。 8. 若第 7 步的 URL 验证未通过,请检查你所配置的地址是否正确。 参考文档: ## 配置项 ### options.appId * 类型:`string` 机器人的应用 ID。 ### options.appSecret * 类型:`string` 机器人的应用密钥。 ### options.encryptKey * 类型:`string` 机器人的 Encrypt Key。 ### options.verificationToken * 类型:`string` 事件推送的验证令牌。 ## HTTP 配置项 ### options.path * 类型:`string` * 默认值:`'/feishu'` 或 `'/lark'` 服务器监听的路径。 ### options.selfUrl * 类型:`string` Koishi 服务暴露在公网的地址,会覆盖 [`app.config.selfUrl`](../../api/core/app.md#options-selfurl) 的值。 ### options.verifyToken * 类型:`boolean` 接受事件推送时是否验证令牌。 ### options.verifySignature * 类型:`boolean` 接受事件推送时是否验证签名。 --- --- url: /plugins/adapter/line.md --- # @koishijs/plugin-adapter-line ::: warning 在接入之前,你需要准备一个带有 SSL 证书的公网域名,并将 Koishi [部署到公网](../../manual/recipe/server.md)。 ::: ## 接入方法 1. 在 [LINE 开发者控制台](https://developers.line.biz/console/) 注册账号,创建一个新的 Provider,在 Provider 中创建一个 Channel,类型选择 Messaging API,输入相关信息 2. 在 Basic settings 页面找到 Channel secret,填入插件的 secret 字段 3. 在 Messaging API 页面底部 Channel access token 处点击 Issue 创建 token,填入插件的 token 字段 4. 根据使用需求可在上方的 Allow bot to join group chats (允许机器人加入群组) 处点击 Edit,在新页面中找到 Toggle features 一栏,第一对单选框选择 Allow 5. 在 Messaging API 页面底部,根据使用需求点击 Auto-reply messages 或者 Greeting messages 的修改按钮,在新页面中可设置是否启用平台自带的自动回复或问候消息 6. 在 Security 页面推荐配置白名单 IP 7. 启动插件,打开 Messaging API 页面,勾选 Use webhook 参考文档: ## 配置项 ### options.token * 类型:`string` 机器人令牌。 ### options.secret * 类型:`string` 机器人密钥。 ## 内部接口 ::: tip 关于内部接口的使用方式,请参见 [访问内部接口](../../guide/adapter/bot.md#access-from-plugin)。 ::: * [`internal.audienceMatch()`](https://developers.line.biz/en/reference/partner-docs/#phone-audience-match) * [`internal.broadcast()`](https://developers.line.biz/en/reference/messaging-api/#send-broadcast-message) * [`internal.cancelDefaultRichMenu()`](https://developers.line.biz/en/reference/messaging-api/#cancel-default-rich-menu) * [`internal.createRichMenu()`](https://developers.line.biz/en/reference/messaging-api/#create-rich-menu) * [`internal.createRichMenuAlias()`](https://developers.line.biz/en/reference/messaging-api/#create-rich-menu-alias) * [`internal.deleteRichMenu()`](https://developers.line.biz/en/reference/messaging-api/#delete-rich-menu) * [`internal.deleteRichMenuAlias()`](https://developers.line.biz/en/reference/messaging-api/#delete-rich-menu-alias) * [`internal.getAdPhoneMessageStatistics()`](https://developers.line.biz/en/reference/partner-docs/#get-phone-audience-match) * [`internal.getAggregationUnitNameList()`](https://developers.line.biz/en/reference/messaging-api/#get-name-list-of-units-used-this-month) * [`internal.getAggregationUnitUsage()`](https://developers.line.biz/en/reference/messaging-api/#get-number-of-units-used-this-month) * [`internal.getBotInfo()`](https://developers.line.biz/en/reference/messaging-api/#get-bot-info) * [`internal.getDefaultRichMenuId()`](https://developers.line.biz/en/reference/messaging-api/#get-default-rich-menu-id) * [`internal.getFollowers()`](https://developers.line.biz/en/reference/messaging-api/#get-follower-ids) * [`internal.getGroupMemberCount()`](https://developers.line.biz/en/reference/messaging-api/#get-members-group-count) * [`internal.getGroupMemberProfile()`](https://developers.line.biz/en/reference/messaging-api/#get-group-member-profile) * [`internal.getGroupMembersIds()`](https://developers.line.biz/en/reference/messaging-api/#get-group-member-user-ids) * [`internal.getGroupSummary()`](https://developers.line.biz/en/reference/messaging-api/#get-group-summary) * [`internal.getMessageContent()`](https://developers.line.biz/en/reference/messaging-api/#get-content) * [`internal.getMessageContentPreview()`](https://developers.line.biz/en/reference/messaging-api/#get-image-or-video-preview) * [`internal.getMessageContentTranscodingByMessageId()`](https://developers.line.biz/en/reference/messaging-api/#verify-video-or-audio-preparation-status) * [`internal.getMessageQuota()`](https://developers.line.biz/en/reference/messaging-api/#get-quota) * [`internal.getMessageQuotaConsumption()`](https://developers.line.biz/en/reference/messaging-api/#get-consumption) * [`internal.getNarrowcastProgress()`](https://developers.line.biz/en/reference/messaging-api/#get-narrowcast-progress-status) * [`internal.getNumberOfSentBroadcastMessages()`](https://developers.line.biz/en/reference/messaging-api/#get-number-of-broadcast-messages) * [`internal.getNumberOfSentMulticastMessages()`](https://developers.line.biz/en/reference/messaging-api/#get-number-of-multicast-messages) * [`internal.getNumberOfSentPushMessages()`](https://developers.line.biz/en/reference/messaging-api/#get-number-of-push-messages) * [`internal.getNumberOfSentReplyMessages()`](https://developers.line.biz/en/reference/messaging-api/#get-number-of-reply-messages) * [`internal.getPNPMessageStatistics()`](https://developers.line.biz/en/reference/partner-docs/#get-number-of-sent-line-notification-messages) * [`internal.getProfile()`](https://developers.line.biz/en/reference/messaging-api/#get-profile) * [`internal.getRichMenu()`](https://developers.line.biz/en/reference/messaging-api/#get-rich-menu) * [`internal.getRichMenuAlias()`](https://developers.line.biz/en/reference/messaging-api/#get-rich-menu-alias-by-id) * [`internal.getRichMenuAliasList()`](https://developers.line.biz/en/reference/messaging-api/#get-rich-menu-alias-list) * [`internal.getRichMenuBatchProgress()`](https://developers.line.biz/en/reference/messaging-api/#get-batch-control-rich-menus-progress-status) * [`internal.getRichMenuIdOfUser()`](https://developers.line.biz/en/reference/messaging-api/#get-rich-menu-id-of-user) * [`internal.getRichMenuImage()`](https://developers.line.biz/en/reference/messaging-api/#download-rich-menu-image) * [`internal.getRichMenuList()`](https://developers.line.biz/en/reference/messaging-api/#get-rich-menu-list) * [`internal.getRoomMemberCount()`](https://developers.line.biz/en/reference/messaging-api/#get-members-room-count) * [`internal.getRoomMemberProfile()`](https://developers.line.biz/en/reference/messaging-api/#get-room-member-profile) * [`internal.getRoomMembersIds()`](https://developers.line.biz/en/reference/messaging-api/#get-room-member-user-ids) * [`internal.getWebhookEndpoint()`](https://developers.line.biz/en/reference/messaging-api/#get-webhook-endpoint-information) * [`internal.issueLinkToken()`](https://developers.line.biz/en/reference/messaging-api/#issue-link-token) * [`internal.leaveGroup()`](https://developers.line.biz/en/reference/messaging-api/#leave-group) * [`internal.leaveRoom()`](https://developers.line.biz/en/reference/messaging-api/#leave-room) * [`internal.linkRichMenuIdToUser()`](https://developers.line.biz/en/reference/messaging-api/#link-rich-menu-to-user) * [`internal.markMessagesAsRead()`](https://developers.line.biz/en/reference/partner-docs/#mark-messages-from-users-as-read) * [`internal.multicast()`](https://developers.line.biz/en/reference/messaging-api/#send-multicast-message) * [`internal.pushMessage()`](https://developers.line.biz/en/reference/messaging-api/#send-push-message) * [`internal.pushMessagesByPhone()`](https://developers.line.biz/en/reference/partner-docs/#send-line-notification-message) * [`internal.replyMessage()`](https://developers.line.biz/en/reference/messaging-api/#send-reply-message) * [`internal.setDefaultRichMenu()`](https://developers.line.biz/en/reference/messaging-api/#set-default-rich-menu) * [`internal.setRichMenuImage()`](https://developers.line.biz/en/reference/messaging-api/#upload-rich-menu-image) * [`internal.setWebhookEndpoint()`](https://developers.line.biz/en/reference/messaging-api/#set-webhook-endpoint-url) * [`internal.testWebhookEndpoint()`](https://developers.line.biz/en/reference/messaging-api/#test-webhook-endpoint) * [`internal.unlinkRichMenuIdFromUser()`](https://developers.line.biz/en/reference/messaging-api/#unlink-rich-menu-from-user) * [`internal.updateRichMenuAlias()`](https://developers.line.biz/en/reference/messaging-api/#update-rich-menu-alias) * [`internal.validateBroadcast()`](https://developers.line.biz/en/reference/messaging-api/#validate-message-objects-of-broadcast-message) * [`internal.validateMulticast()`](https://developers.line.biz/en/reference/messaging-api/#validate-message-objects-of-multicast-message) * [`internal.validateNarrowcast()`](https://developers.line.biz/en/reference/messaging-api/#validate-message-objects-of-narrowcast-message) * [`internal.validatePush()`](https://developers.line.biz/en/reference/messaging-api/#validate-message-objects-of-push-message) * [`internal.validateReply()`](https://developers.line.biz/en/reference/messaging-api/#validate-message-objects-of-reply-message) * [`internal.validateRichMenuBatchRequest()`](https://developers.line.biz/en/reference/messaging-api/#validate-batch-control-rich-menus-request) * [`internal.validateRichMenuObject()`](https://developers.line.biz/en/reference/messaging-api/#validate-rich-menu-object) ## 内部事件 * [`line/account-link`](https://developers.line.biz/en/reference/messaging-api/#account-link-event) * [`line/beacon`](https://developers.line.biz/en/reference/messaging-api/#beacon-event) * [`line/device-link`](https://developers.line.biz/en/reference/messaging-api/#device-link-event) * [`line/device-unlink`](https://developers.line.biz/en/reference/messaging-api/#device-unlink-event) * [`line/follow`](https://developers.line.biz/en/reference/messaging-api/#follow-event) * [`line/join`](https://developers.line.biz/en/reference/messaging-api/#join-event) * [`line/leave`](https://developers.line.biz/en/reference/messaging-api/#leave-event) * [`line/member-joined`](https://developers.line.biz/en/reference/messaging-api/#member-joined-event) * [`line/member-left`](https://developers.line.biz/en/reference/messaging-api/#member-left-event) * [`line/message`](https://developers.line.biz/en/reference/messaging-api/#message-event) * [`line/postback`](https://developers.line.biz/en/reference/messaging-api/#postback-event) * [`line/scenario-result`](https://developers.line.biz/en/reference/messaging-api/#scenario-result-event) * [`line/unfollow`](https://developers.line.biz/en/reference/messaging-api/#unfollow-event) * [`line/unsend`](https://developers.line.biz/en/reference/messaging-api/#unsend-event) * [`line/video-viewing-complete`](https://developers.line.biz/en/reference/messaging-api/#video-viewing-complete) --- --- url: /plugins/adapter/mail.md --- # @koishijs/plugin-adapter-mail ## 接入方式 1. 「username」对应你的邮箱账号,「password」对应你的授权码 2. 「imap」对应接收服务器,「smtp」对应发送服务器,需要分别填写对应的「host」和「port」 3. 不同邮箱服务获取授权码的方式也有所不同,可以参考下面的主流邮件服务进行配置 ### QQ 邮箱 * 接收服务器:`imap.qq.com`,端口号 `993` * 发送服务器:`smtp.qq.com`,端口号 `465` 或 `587` * 参考:[什么是授权码,它又是如何设置?](https://service.mail.qq.com/detail/0/75) ### 网易 163 邮箱 ::: danger 由于 [satorijs/satori#204](https://github.com/satorijs/satori/issues/204),暂时无法正常接入网易 (163, 126) 邮箱。 ::: * 接收服务器:`imap.163.com`,端口号 `993` * 发送服务器:`smtp.163.com`,端口号 `465` 或 `994` * 参考:[网易邮箱 IMAP 服务](https://mail.163.com/html/110127_imap/index.htm) ### Outlook ::: danger [Outlook 现仅支持 OAuth 验证](https://learn.microsoft.com/en-us/exchange/clients-and-mobile-in-exchange-online/deprecation-of-basic-authentication-exchange-online)。 ::: * 接收服务器:`outlook.office365.com`,端口号 `993` * 发送服务器:`smtp-mail.outlook.com`,端口号 `587` * 参考:[Outlook.com 的 POP、IMAP 和 SMTP 设置](https://support.microsoft.com/zh-cn/office/outlook-com-%E7%9A%84-pop-imap-%E5%92%8C-smtp-%E8%AE%BE%E7%BD%AE-d088b986-291d-42b8-9564-9c414e2aa040) ### Gmail * 接收服务器:`imap.gmail.com`,端口号 `993` * 发送服务器:`smtp.gmail.com`,端口号 `465` * 参考:[通过其他电子邮件平台查看 Gmail](https://support.google.com/mail/answer/7126229?hl=zh-Hans#zippy=%2C%E7%AC%AC-%E6%AD%A5%E6%A3%80%E6%9F%A5-imap-%E6%98%AF%E5%90%A6%E5%B7%B2%E5%90%AF%E7%94%A8%2C%E7%AC%AC-%E6%AD%A5%E5%9C%A8%E7%94%B5%E5%AD%90%E9%82%AE%E4%BB%B6%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%B8%AD%E6%9B%B4%E6%94%B9-smtp-%E5%92%8C%E5%85%B6%E4%BB%96%E8%AE%BE%E7%BD%AE) ## 配置项 ### options.username * 类型: `string` * 必需参数 用户名。 ### options.password * 类型: `string` * 必需参数 密码或授权码。 ### options.selfId * 类型: `string` * 默认值: [`options.username`](#options-username) 邮件地址 (默认与用户名相同)。 ### options.subject * 类型: `string` 机器人发送的邮件主题。 ### options.imap * **imap.host:** `string` 服务器地址 * **imap.port:** `number` 服务器端口 * **imap.tls:** `boolean` 是否开启 TLS 加密 接收服务器配置。 ### options.smtp * **smtp.host:** `string` 服务器地址 * **smtp.port:** `number` 服务器端口 * **smtp.tls:** `boolean` 是否开启 TLS 加密 发送服务器配置。 --- --- url: /plugins/adapter/matrix.md --- # @koishijs/plugin-adapter-matrix ::: warning 在接入之前,你需要准备一个带有 SSL 证书的公网域名,并将 Koishi [部署到公网](../../manual/recipe/server.md)。 ::: ## 接入方式 1. 参考 [此链接](https://spec.matrix.org/unstable/application-service-api/#registration) 编写 `registry.yaml` 文件: ```yaml id: koishi # Application Service 的 ID hs_token: # 填入任意内容,与配置文件相对应,请确保不会泄漏 as_token: # 填入任意内容,与配置文件相对应,请确保不会泄漏 url: # 你的机器人地址,通常是 {selfUrl}/matrix sender_localpart: koishi # 不能与机器人的 ID 相同 namespaces: users: - exclusive: true # 这里填入你的机器人的 userId # 如果需要同时接入多个 matrix 机器人,请使用正则表达式 regex: '@koishi:matrix.example.com' ``` 2. 将 `registry.yaml` 添加进你的服务器 (如 synapse 则使用 `app_service_config_files` 配置项来指向 `registry.yaml` 并重启服务器) 3. 在控制台中配置本插件,`host` 填入你的 Homeserver 域名,`hs_token`, `as_token` 上述文件中的对应值,`id` 填入任意值 (需要与 `sender_localpart` 不同) 4. 安装 [koishi-plugin-verifier](https://common.koishi.chat/plugins/verifier.html) (或其他自助通过群组邀请的插件) 5. 在房间中邀请机器人 (机器人的 ID 为 `@${id}:${host}`) ## 配置项 ### options.id * 类型: `string` * 必需参数 机器人的 ID。机器人最后的用户名将会是 `@{id}:{host}`。请注意不能与上面的 `sender_localpart` 相同。 ### options.host * 类型: `string` * 必需参数 Matrix Homeserver 域名。 ### options.hsToken * 类型: `string` * 必需参数 与上述文件中的 `hs_token` 对应。 ### options.asToken * 类型: `string` * 必需参数 与上述文件中的 `as_token` 对应。 ### options.endpoint * 类型: `string` Matrix Homeserver 地址。默认为 `https://{host}`。 ### options.name * 类型: `string` 机器人的名称,如果设置了将会在启动时为机器人更改。 ### options.avatar * 类型: `string` 机器人的头像地址,如果设置了将会在启动时为机器人更改。 --- --- url: /plugins/adapter/qq.md --- # @koishijs/plugin-adapter-qq QQ 和 QQ 频道官方机器人适配器。 ## 接入方法 1. 前往 [QQ 开放平台](https://q.qq.com) 注册账号 2. 登陆进入 [机器人管理后台](https://q.qq.com/#/app/bot) 并创建官方机器人 3. 创建完成后,在「开发设置」界面获取机器人三项基本数据 \[id, token, key] 4. 将上面的基本数据作为机器人配置项即可使用 ## 机器人选项 ### config.id * 类型: `string` 机器人 id。 ### config.key * 类型: `string` 机器人密钥,管理端又称呼为 secret。 ### config.token * 类型: `string` 机器人 token。 ### config.type * 类型: `'private' | 'public'` 是否为公域机器人。 ### config.sandbox * 类型: `boolean` * 默认值: `true` 是否开启沙盒。 ### config.endpoint * 类型: `string` * 默认值: `'https://api.sgroup.qq.com/'` 要请求的 API 网址。 ### config.authType * 类型: `'bot' | 'bearer'` * 默认值: `'bot'` 验证方式。 ## 内部接口 ::: tip 关于内部接口的使用方式,请参见 [访问内部接口](../../guide/adapter/bot.md#access-from-plugin)。 ::: QQ 群和频道有着不同的机器人接口,因此我们提供了两套内部 API。 ### 群 (含私聊) 对应的平台名称为 `qq`。 * `internal.acknowledgeInteraction()` * `internal.sendFileGuild()` * `internal.sendFilePrivate()` * `internal.sendMessage()` * `internal.sendPrivateMessage()` ### 频道 (含私聊) 对应的平台名称为 `qqguild`。 * `internal.addGuildMemberRole()` * `internal.createDMS()` * `internal.createGuildApiPermissionDemand()` * `internal.createGuildAnnounce()` * `internal.createGuildChannel()` * `internal.createGuildRole()` * `internal.createPinsMessage()` * `internal.createPost()` * `internal.createSchedule()` * `internal.createReaction()` * `internal.deleteChannel()` * `internal.deleteDM()` * `internal.deleteMessage()` * `internal.deleteReaction()` * `internal.getChannel()` * `internal.getChannelMemberPermissions()` * `internal.getChannelOnlineNums()` * `internal.getChannelRole()` * `internal.getChannels()` * `internal.getGuild()` * `internal.getGuildApiPermissions()` * `internal.getGuildMember()` * `internal.getGuildMembers()` * `internal.getGuildRoleMembers()` * `internal.getGuildRoles()` * `internal.getGuilds()` * `internal.getMe()` * `internal.getMessage()` * `internal.getMessageSetting()` * `internal.getPinsMessage()` * `internal.getSchedule()` * `internal.getSchedules()` * `internal.getReactions()` * `internal.getThread()` * `internal.listThreads()` * `internal.modifyChannel()` * `internal.modifyChannelMemberPermissions()` * `internal.modifyChannelRole()` * `internal.modifyGuildRole()` * `internal.modifySchedule()` * `internal.muteGuild()` * `internal.muteGuildMember()` * `internal.muteGuildMembers()` * `internal.removeGuildAnnounce()` * `internal.removeGuildMember()` * `internal.removeGuildMemberRole()` * `internal.removeGuildRole()` * `internal.removePinsMessage()` * `internal.removePost()` * `internal.removeSchedule()` * `internal.sendDM()` * `internal.sendMessage()` --- --- url: /plugins/adapter/satori.md --- # @koishijs/plugin-adapter-satori ::: tip 另见:[@koishijs/plugin-server-satori](../develop/server-satori.md)。 ::: ## 接入方式 填写下方的配置项即可。 ## 配置项 ### config.endpoint * 类型: `string` * 必需参数 Satori 服务器的地址。 --- --- url: /plugins/adapter/slack.md --- # @koishijs/plugin-adapter-slack ::: tip 如果选择通信方式为 `http`,那么你需要准备一个带有 SSL 证书的公网域名,并将 Koishi [部署到公网](../../manual/recipe/server.md)。 ::: ## 接入方式 Slack 适配器提供了两种接入方式:`http` 和 `ws` (推荐)。其中,`http` 连接方式需要一个公网可访问的地址。在接入之前,请根据使用需求填写插件的 [`protocol`](#config-protocol) 字段。 1. 准备一个 [Slack](https://slack.com/signin) 账号和工作区。 2. 前往 [应用后台](https://api.slack.com/apps),点击「Create New App」>「From Scratch」,填入应用名称和所添加的工作区 (目前适配器只支持一个应用处理一个工作区的事件),点击「Create App」。 ![app-1](/adapter/slack/app-1.webp) 3. 仅限 `http` 连接方式:在跳转至的应用信息页面,在底部复制 `Signing Secret`,填入插件的 [`signing`](#config-signing) 字段。 ![secret-1](/adapter/slack/secret-1.webp) 4. 在「App-Level Tokens」一栏,点击带有 Generate 字样的按钮,填写 Token 名称,在下方下拉框中选择 `connections:write`,点击绿色 Generate 按钮,在弹出的对话框中点击 Copy 按钮,填入插件的 [`token`](#config-token) 字段。 ![secret-2](/adapter/slack/secret-2.webp) 5. 仅限 `http` 连接方式:在左侧打开「Event Subscriptions」页面,勾选「Enable Events」。将机器人的 `selfUrl` 值后连接 `/slack` (如 `https://example.com/slack`),填入「Request URL」中。 ![webhook](/adapter/slack/webhook.webp) 6. 仅限 `ws` 连接方式:在左侧打开「Socket Mode」页面,勾选「Enable Socket Mode」;再打开「Event Subscriptions」页面,勾选「Enable Events」。 ![websocket](/adapter/slack/websocket.webp) 7. 前往「Event Subscriptions」页面,在「Subscribe to bot events」下方勾选事件 (参见 [推荐的事件列表](#推荐的事件列表)),填写完整后点击右下角的绿色「Save Changes」按钮保存。 ![events](/adapter/slack/events.webp) 8. 前往「OAuth & Permissions」页面,在「Bot Token Scopes」下方添加权限 (参见 [推荐的权限列表](#推荐的权限列表))。 ![scopes](/adapter/slack/scopes.webp) 9. 返回页面上方,点击「Install to Workspace」,点击 Allow 授权,复制「Bot User OAuth Token」,填入插件的 [`botToken`](#config-bottoken) 字段。 ![workspace](/adapter/slack/workspace.webp) 10. 在相应工作区 @ 机器人名称或右键频道详情,选择 集成-添加应用 添加机器人到频道中。 ### 推荐的事件列表 * channel\_archive * channel\_created * channel\_deleted * channel\_left * channel\_rename * member\_joined\_channel * member\_left\_channel * message.channels * message.groups * message.im * reaction\_added * reaction\_removed * team\_join ### 推荐的权限列表 * channels:history * channels:read * channels:write.invites * chat:write * chat:write.customize * chat:write.public * files:read * files:write * groups:history * groups:read * groups:write * im:history * im:write * reactions:read * reactions:write * users:read ## 配置项 ### config.protocol * 可选值: http, ws 要使用的协议类型。 ### config.token * 类型: `string` * 必需选项 应用令牌。 ### config.botToken * 类型: `string` * 必需选项 机器人令牌。 ## HTTP 配置项 ### config.signing * 类型: `string` * 必需选项 用于验证请求来源的签名密钥。 ## WS 配置项 包括全部的 [`WsClient`](../../api/core/adapter.md#类-adapter-wsclient) 选项。 --- --- url: /plugins/adapter/telegram.md --- # @koishijs/plugin-adapter-telegram ::: tip 如果选择通信方式为 `http`,那么你需要准备一个带有 SSL 证书的公网域名,并将 Koishi [部署到公网](../../manual/recipe/server.md)。 ::: ## 接入方法 1. 搜索 @botfather(有个官方认证的符号)并进入聊天界面 2. 输入 `/start` 后,会出现一个使用菜单,你可以使用这里指令对你的机器人进行配置 3. 要创建一个机器人,请点击 `/newbot`,并根据系统提示完成创建流程 4. 使用 `/setprivacy` 关闭 Privacy Mode(设置为 DISABLED,不然机器人只能收到特定消息) 5. 创建完毕后,你会获得一个 token(请注意不要泄露),将其作为机器人配置项即可使用 参考文档: ::: tip 如果启动机器人后发现收不到不带 `/` 的消息,这很可能是由于 Privacy Mode 未开启。如果开启后仍然收不到消息,请尝试将机器人从群组中移除后重新加入。 ::: ## 机器人选项 ### options.protocol * 可选值: server, polling 要使用的协议类型。 ### options.token * 类型: `string` 机器人账户的令牌。 ### options.endpoint * 类型: `string` * 默认值: `'https://api.telegram.org'` 要连接的服务器地址。 ### options.proxyAgent * 类型: `string` * 默认值: [`app.config.request.proxyAgent`](../../api/core/app.md#options-request-proxyagent) 请求时默认使用的网络代理。 ### options.files.endpoint * 类型: `string` * 默认值: [`options.endpoint`](#options-bots-endpoint) 文件请求的终结点。 ### options.files.local * 类型: `boolean` * 默认值: `false` 是否启用 [Telegram Bot API](https://github.com/tdlib/telegram-bot-api) 本地模式。 ## 适配器选项 ### options.path * 类型:`string` * 默认值:`'/telegram'` 服务器监听的路径。 ### options.selfUrl * 类型:`string` Koishi 服务暴露在公网的地址,会覆盖 [`app.config.selfUrl`](../../api/core/app.md#options-selfurl) 的值。 ## 内部接口 ::: tip 关于内部接口的使用方式,请参见 [访问内部接口](../../guide/adapter/bot.md#access-from-plugin)。 ::: * [`internal.addStickerToSet()`](https://core.telegram.org/bots/api#addstickertoset) * [`internal.answerCallbackQuery()`](https://core.telegram.org/bots/api#answercallbackquery) * [`internal.answerInlineQuery()`](https://core.telegram.org/bots/api#answerinlinequery) * [`internal.answerPreCheckoutQuery()`](https://core.telegram.org/bots/api#answerprecheckoutquery) * [`internal.answerShippingQuery()`](https://core.telegram.org/bots/api#answershippingquery) * [`internal.answerWebAppQuery()`](https://core.telegram.org/bots/api#answerwebappquery) * [`internal.approveChatJoinRequest()`](https://core.telegram.org/bots/api#approvechatjoinrequest) * [`internal.banChatMember()`](https://core.telegram.org/bots/api#banchatmember) * [`internal.banChatSenderChat()`](https://core.telegram.org/bots/api#banchatsenderchat) * [`internal.close()`](https://core.telegram.org/bots/api#close) * [`internal.closeForumTopic()`](https://core.telegram.org/bots/api#closeforumtopic) * [`internal.closeGeneralForumTopic()`](https://core.telegram.org/bots/api#closegeneralforumtopic) * [`internal.copyMessage()`](https://core.telegram.org/bots/api#copymessage) * [`internal.createChatInviteLink()`](https://core.telegram.org/bots/api#createchatinvitelink) * [`internal.createForumTopic()`](https://core.telegram.org/bots/api#createforumtopic) * [`internal.createInvoiceLink()`](https://core.telegram.org/bots/api#createinvoicelink) * [`internal.createNewStickerSet()`](https://core.telegram.org/bots/api#createnewstickerset) * [`internal.declineChatJoinRequest()`](https://core.telegram.org/bots/api#declinechatjoinrequest) * [`internal.deleteChatPhoto()`](https://core.telegram.org/bots/api#deletechatphoto) * [`internal.deleteChatStickerSet()`](https://core.telegram.org/bots/api#deletechatstickerset) * [`internal.deleteForumTopic()`](https://core.telegram.org/bots/api#deleteforumtopic) * [`internal.deleteMessage()`](https://core.telegram.org/bots/api#deletemessage) * [`internal.deleteMyCommands()`](https://core.telegram.org/bots/api#deletemycommands) * [`internal.deleteStickerFromSet()`](https://core.telegram.org/bots/api#deletestickerfromset) * [`internal.deleteWebhook()`](https://core.telegram.org/bots/api#deletewebhook) * [`internal.editChatInviteLink()`](https://core.telegram.org/bots/api#editchatinvitelink) * [`internal.editForumTopic()`](https://core.telegram.org/bots/api#editforumtopic) * [`internal.editGeneralForumTopic()`](https://core.telegram.org/bots/api#editgeneralforumtopic) * [`internal.editMessageCaption()`](https://core.telegram.org/bots/api#editmessagecaption) * [`internal.editMessageLiveLocation()`](https://core.telegram.org/bots/api#editmessagelivelocation) * [`internal.editMessageMedia()`](https://core.telegram.org/bots/api#editmessagemedia) * [`internal.editMessageReplyMarkup()`](https://core.telegram.org/bots/api#editmessagereplymarkup) * [`internal.editMessageText()`](https://core.telegram.org/bots/api#editmessagetext) * [`internal.exportChatInviteLink()`](https://core.telegram.org/bots/api#exportchatinvitelink) * [`internal.forwardMessage()`](https://core.telegram.org/bots/api#forwardmessage) * [`internal.getChat()`](https://core.telegram.org/bots/api#getchat) * [`internal.getChatAdministrators()`](https://core.telegram.org/bots/api#getchatadministrators) * [`internal.getChatMember()`](https://core.telegram.org/bots/api#getchatmember) * [`internal.getChatMemberCount()`](https://core.telegram.org/bots/api#getchatmembercount) * [`internal.getChatMenuButton()`](https://core.telegram.org/bots/api#getchatmenubutton) * [`internal.getCustomEmojiStickers()`](https://core.telegram.org/bots/api#getcustomemojistickers) * [`internal.getFile()`](https://core.telegram.org/bots/api#getfile) * [`internal.getForumTopicIconStickers()`](https://core.telegram.org/bots/api#getforumtopiciconstickers) * [`internal.getGameHighScores()`](https://core.telegram.org/bots/api#getgamehighscores) * [`internal.getMe()`](https://core.telegram.org/bots/api#getme) * [`internal.getMyCommands()`](https://core.telegram.org/bots/api#getmycommands) * [`internal.getMyDefaultAdministratorRights()`](https://core.telegram.org/bots/api#getmydefaultadministratorrights) * [`internal.getStickerSet()`](https://core.telegram.org/bots/api#getstickerset) * [`internal.getUpdates()`](https://core.telegram.org/bots/api#getupdates) * [`internal.getUserProfilePhotos()`](https://core.telegram.org/bots/api#getuserprofilephotos) * [`internal.getWebhookInfo()`](https://core.telegram.org/bots/api#getwebhookinfo) * [`internal.hideGeneralForumTopic()`](https://core.telegram.org/bots/api#hidegeneralforumtopic) * [`internal.leaveChat()`](https://core.telegram.org/bots/api#leavechat) * [`internal.logOut()`](https://core.telegram.org/bots/api#logout) * [`internal.pinChatMessage()`](https://core.telegram.org/bots/api#pinchatmessage) * [`internal.promoteChatMember()`](https://core.telegram.org/bots/api#promotechatmember) * [`internal.reopenForumTopic()`](https://core.telegram.org/bots/api#reopenforumtopic) * [`internal.reopenGeneralForumTopic()`](https://core.telegram.org/bots/api#reopengeneralforumtopic) * [`internal.restrictChatMember()`](https://core.telegram.org/bots/api#restrictchatmember) * [`internal.revokeChatInviteLink()`](https://core.telegram.org/bots/api#revokechatinvitelink) * [`internal.sendAnimation()`](https://core.telegram.org/bots/api#sendanimation) * [`internal.sendAudio()`](https://core.telegram.org/bots/api#sendaudio) * [`internal.sendChatAction()`](https://core.telegram.org/bots/api#sendchataction) * [`internal.sendContact()`](https://core.telegram.org/bots/api#sendcontact) * [`internal.sendDice()`](https://core.telegram.org/bots/api#senddice) * [`internal.sendDocument()`](https://core.telegram.org/bots/api#senddocument) * [`internal.sendGame()`](https://core.telegram.org/bots/api#sendgame) * [`internal.sendInvoice()`](https://core.telegram.org/bots/api#sendinvoice) * [`internal.sendLocation()`](https://core.telegram.org/bots/api#sendlocation) * [`internal.sendMediaGroup()`](https://core.telegram.org/bots/api#sendmediagroup) * [`internal.sendMessage()`](https://core.telegram.org/bots/api#sendmessage) * [`internal.sendPhoto()`](https://core.telegram.org/bots/api#sendphoto) * [`internal.sendPoll()`](https://core.telegram.org/bots/api#sendpoll) * [`internal.sendSticker()`](https://core.telegram.org/bots/api#sendsticker) * [`internal.sendVenue()`](https://core.telegram.org/bots/api#sendvenue) * [`internal.sendVideo()`](https://core.telegram.org/bots/api#sendvideo) * [`internal.sendVideoNote()`](https://core.telegram.org/bots/api#sendvideonote) * [`internal.sendVoice()`](https://core.telegram.org/bots/api#sendvoice) * [`internal.setChatAdministratorCustomTitle()`](https://core.telegram.org/bots/api#setchatadministratorcustomtitle) * [`internal.setChatDescription()`](https://core.telegram.org/bots/api#setchatdescription) * [`internal.setChatMenuButton()`](https://core.telegram.org/bots/api#setchatmenubutton) * [`internal.setChatPermissions()`](https://core.telegram.org/bots/api#setchatpermissions) * [`internal.setChatPhoto()`](https://core.telegram.org/bots/api#setchatphoto) * [`internal.setChatStickerSet()`](https://core.telegram.org/bots/api#setchatstickerset) * [`internal.setChatTitle()`](https://core.telegram.org/bots/api#setchattitle) * [`internal.setGameScore()`](https://core.telegram.org/bots/api#setgamescore) * [`internal.setMyCommands()`](https://core.telegram.org/bots/api#setmycommands) * [`internal.setMyDefaultAdministratorRights()`](https://core.telegram.org/bots/api#setmydefaultadministratorrights) * [`internal.setPassportDataErrors()`](https://core.telegram.org/bots/api#setpassportdataerrors) * [`internal.setStickerPositionInSet()`](https://core.telegram.org/bots/api#setstickerpositioninset) * [`internal.setStickerSetThumb()`](https://core.telegram.org/bots/api#setstickersetthumb) * [`internal.setWebhook()`](https://core.telegram.org/bots/api#setwebhook) * [`internal.stopMessageLiveLocation()`](https://core.telegram.org/bots/api#stopmessagelivelocation) * [`internal.stopPoll()`](https://core.telegram.org/bots/api#stoppoll) * [`internal.unbanChatMember()`](https://core.telegram.org/bots/api#unbanchatmember) * [`internal.unbanChatSenderChat()`](https://core.telegram.org/bots/api#unbanchatsenderchat) * [`internal.unhideGeneralForumTopic()`](https://core.telegram.org/bots/api#unhidegeneralforumtopic) * [`internal.unpinAllChatMessages()`](https://core.telegram.org/bots/api#unpinallchatmessages) * [`internal.unpinAllForumTopicMessages()`](https://core.telegram.org/bots/api#unpinallforumtopicmessages) * [`internal.unpinChatMessage()`](https://core.telegram.org/bots/api#unpinchatmessage) * [`internal.uploadStickerFile()`](https://core.telegram.org/bots/api#uploadstickerfile) ## 内部事件 * [`telegram/callback-query`](https://core.telegram.org/bots/api#update) * [`telegram/channel-post`](https://core.telegram.org/bots/api#update) * [`telegram/chat-join-request`](https://core.telegram.org/bots/api#update) * [`telegram/chat-member`](https://core.telegram.org/bots/api#update) * [`telegram/chosen-inline-result`](https://core.telegram.org/bots/api#update) * [`telegram/edited-channel-post`](https://core.telegram.org/bots/api#update) * [`telegram/edited-message`](https://core.telegram.org/bots/api#update) * [`telegram/inline-query`](https://core.telegram.org/bots/api#update) * [`telegram/message`](https://core.telegram.org/bots/api#update) * [`telegram/my-chat-member`](https://core.telegram.org/bots/api#update) * [`telegram/poll-answer`](https://core.telegram.org/bots/api#update) * [`telegram/poll`](https://core.telegram.org/bots/api#update) * [`telegram/pre-checkout-query`](https://core.telegram.org/bots/api#update) * [`telegram/shipping-query`](https://core.telegram.org/bots/api#update) --- --- url: /plugins/adapter/wechat-official.md --- # @koishijs/plugin-adapter-wechat-official ::: warning 在接入之前,你需要准备一个带有 SSL 证书的公网域名,并将 Koishi [部署到公网](../../manual/recipe/server.md)。 ::: ## 接入方法 1. 根据 [注册流程指引](https://kf.qq.com/product/weixinmp.html#hid=87) 注册公众平台。 2. 在微信公众平台登录后,页面左侧展开「设置与开发」,进入「公众号设置」,翻至页面底部,复制 `原始 ID` 填入插件的 account 字段。 3. 页面左侧进入「基本配置」,复制 `开发者ID` 填入插件的 appId 字段,在网页上获取开发者密码填入插件的 secret 字段,设置白名单 IP。 4. 页面下方启用服务器配置,将机器人的 `selfUrl` 值后连接 `/wechat-official` (如 `https://example.com/wechat-official`),在 URL 一栏中填写;在插件配置和公众平台上填入相同的 Token;在公众平台上生成 EncodingAESKey 并填入插件的 aesKey 字段;三种消息加解密方式均可选择。 5. 如果公众号为企业主体,且通过了微信认证,可在插件配置中启用 customerService。客服接口提供了更宽松的消息回复能力。 参考文档: ## 配置项 ### config.account * 类型: `string` 公众号原始 ID。 ### config.appId * 类型: `string` AppID。 ### config.secret * 类型: `string` AppSecret。 ### config.token * 类型: `string` Webhook Token。 ### config.aesKey * 类型: `string` EncodingAESKey。 ### config.customerService * 类型: `boolean` 启用客服消息回复。 --- --- url: /plugins/adapter/wecom.md --- # @koishijs/plugin-adapter-wecom ::: warning 在接入之前,你需要准备一个带有 SSL 证书的公网域名,并将 Koishi [部署到公网](../../manual/recipe/server.md)。 ::: ## 接入方法 1. 前往 [我的企业](https://work.weixin.qq.com/wework_admin/frame#profile),复制页面下方企业 ID,填入插件的 corpId。 2. 前往 [应用管理](https://work.weixin.qq.com/wework_admin/frame#apps) 页面下方点击创建应用,填写应用信息。 3. 复制 `AgentId` 填入插件的 agentId 字段,查看 Secret 填入插件的 `secret` 字段。 4. 在下方功能板块点击「设置 API 接收」,URL 填写机器人的 selfUrl 值后连接 `/wecom`,Token 和 EncodingAESKey 点击随机获取,分别填入插件的 `token` 和 `aesKey` 字段。先启用插件,再在「API 接收消息」页面点击保存. 5. 于页面左上角返回应用页面,在下方开发者接口板块点击「企业可信 IP」的「配置」,设置白名单 IP,确定后稍等几分钟即可使用插件。 6. 推荐在几分钟后停用并启用一次插件,以便加载出平台信息呈现在控制台内。 参考文档: ## 配置项 ### config.corpId * 类型: `string` 企业 ID。 ### config.agentId * 类型: `string` AgentID。 ### config.secret * 类型: `string` AppSecret。 ### config.token * 类型: `string` Webhook Token。 ### config.aesKey * 类型: `string` EncodingAESKey。 --- --- url: /plugins/adapter/whatsapp.md --- # @koishijs/plugin-adapter-whatsapp ::: warning 在接入之前,你需要准备一个带有 SSL 证书的公网域名,并将 Koishi [部署到公网](../../manual/recipe/server.md)。 ::: ## 接入方式 1. 前往 [商务平台](https://business.facebook.com/) 创建业务账户,并在 [业务设置](https://business.facebook.com/settings/security) 完成组织验证 2. 参照 [官方入门指南](https://developers.facebook.com/docs/whatsapp/cloud-api/get-started) 进行操作 3. 阅读 [Business API 概览](https://developers.facebook.com/docs/whatsapp/business-management-api/get-started),创建系统用户访问口令,在 Available Permissions 中勾选 `whatsapp_business_messaging` 和 `whatsapp_business_management`,填入插件的 `systemToken` 字段。在当前页面,点击 Add assets,在左侧 Apps 菜单选中创建的 App,勾选 Develop app,点击 Save changes 4. 在 [应用面板](https://developers.facebook.com),左侧切换至 设置-基本 页面,点击应用密钥右侧的显示按钮,复制密钥填入插件的 `systemToken` 字段。 5. 在 [商业账号](https://business.facebook.com/settings/whatsapp-business-accounts/) 页面选择生产或开发环境的账号集合,复制页面上方高亮的 ID,填入插件的 `id` 字段 6. 在官方入门指南第三步配置 Webhooks 中,Callback URL 填写机器人的 `selfUrl` 值后连接 `/whatsapp`,Verify token 可填写随机的字符串,与插件的 verifyToken 配置保持一致,启用插件后点击 Verify and save 7. 点击 Webhook 字段右侧的管理,在 messages 一行打勾 ## 配置项 ### config.id * 类型:`string` * 必需选项 商户 ID。 ### config.secret * 类型:`string` * 必需选项 应用密钥。 ### config.systemToken * 类型:`string` * 必需选项 系统用户访问令牌。 ### config.verifyToken * 类型:`string` * 必需选项 Webhook 验证令牌。 --- --- url: /plugins/adapter/zulip.md --- # @koishijs/plugin-adapter-zulip ## 接入指南 1. 登录自己的 Zulip 社群,右上角设置按钮选择「个人设置」,在弹窗左侧进入「机器人」,点击「新增机器人」输入相关信息后点击新增。 2. 在界面中找到新创建的机器人,复制机器人邮箱填入插件的 `email` 字段,复制 API KEY 填入插件的 `key` 字段。 ## 配置项 ### config.email * 类型: `string` * 必需字段 机器人邮箱。 ### config.key * 类型: `string` * 必需字段 机器人 API 密钥。 --- --- url: /plugins/database/memory.md --- # @koishijs/plugin-database-memory ## 配置项 此插件暂无配置项。 --- --- url: /plugins/database/mongo.md --- # @koishijs/plugin-database-mongo ## 配置项 ### config.protocol * 类型: `string` * 默认值: `'mongodb'` 要使用的协议。 ### config.host * 类型: `string` * 默认值: `'localhost'` 要连接的主机地址。 ### config.port * 类型: `number` * 默认值: `27017` 要连接的端口号。 ### config.username * 类型: `string` 要使用的用户名。 ### config.password * 类型: `string` 要使用的密码。 ### config.database * 类型: `string` * 默认值: `'koishi'` 要访问的数据库名称。 --- --- url: /plugins/database/mysql.md --- # @koishijs/plugin-database-mysql ::: tip 需要的最低版本是 MySQL 5.7 / 8.0 或 MariaDB 10.5。 ::: ## 配置项 ### config.host * 类型: `string` * 默认值: `'localhost'` 要连接的主机名。 ### config.port * 类型: `number` * 默认值: `3306` 要连接的端口号。 ### config.username * 类型: `string` * 默认值: `'root'` 要使用的用户名。 ### config.password * 类型: `string` 要使用的密码。 ### config.database * 类型: `string` * 默认值: `'koishi'` 要访问的数据库名称。 --- --- url: /plugins/database/postgres.md --- # @koishijs/plugin-database-postgres ## 配置项 ### config.host * 类型: `string` * 默认值: `'localhost'` 要连接的主机名。 ### config.port * 类型: `number` * 默认值: `5432` 要连接的端口号。 ### config.username * 类型: `string` * 默认值: `'root'` 要使用的用户名。 ### config.password * 类型: `string` 要使用的密码。 ### config.database * 类型: `string` * 默认值: `'koishi'` 要访问的数据库名称。 --- --- url: /plugins/database/sqlite.md --- # @koishijs/plugin-database-sqlite ## 配置项 ### config.path * 类型: `string` * 默认值: `'data/koishi.db'` 数据库文件的路径。 --- --- url: /plugins/develop/hmr.md --- # @koishijs/plugin-hmr ::: tip 使用方法请参见 [开发 > 开发起步 > 启动脚本](../../guide/develop/script.md#模块热替换) 章节。 ::: ## 配置项 ### base * 类型:`string` * 默认值:`process.cwd()` 用于监听和显示的基础目录。检测到的文件路径会被转换为相对 `base` 的路径输出。 ### root * 类型:`string | string[]` * 默认值:`['.']` 用于监听的文件或目录列表。支持填写绝对路径或是相对 `base` 的路径。 ### ignore * 类型:`string | string[]` * 默认值:`['**/node_modules/**', '**/.git/**', '**/logs/**']` 要忽略的文件或目录。支持 [Glob Patterns](https://github.com/micromatch/micromatch) 语法。 ### debounce * 类型:`number` * 默认值:`100` 延迟触发更新的等待时间。 --- --- url: /plugins/develop/http.md --- # @koishijs/plugin-http ::: tip 如果想要使用代理,可以使用 [@koishijs/plugin-proxy-agent](./proxy-agent.md) 插件。 ::: @koishijs/plugin-http 提供了 `ctx.http` 基础服务,其上封装了一套基于 [fetch](https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API) 的网络请求 API。 你可能会有这样的疑问:为什么不直接使用 fetch,而是使用 `ctx.http`?这是因为许多插件都需要发起网络请求,而诸如代理、超时等配置又通常是插件无关的。因此我们为这些通用需求提供了统一的配置,各个插件则只需要调用 `ctx.http` 即可,不用关心复杂多变的用户需求。 ## 实例方法 ### ctx.http(method, url, config) * **method:** `string` 请求方法 * **url:** `string` 请求地址 * **config:** `RequestConfig` 配置项 * 返回值: `Promise` ### ctx.http.head(url, config) ### ctx.http.get(url, config) ### ctx.http.delete(url, config) * **url:** `string` 请求地址 * **config:** `RequestConfig` 配置项 * 返回值: `Promise` 发送 HEAD / GET / DELETE 请求。 ### ctx.http.post(url, data, config) ### ctx.http.put(url, data, config) ### ctx.http.patch(url, data, config) * **url:** `string` 请求地址 * **data:** `any` 请求数据 * **config:** `RequestConfig` 配置项 * 返回值: `Promise` 发送 POST / PUT / PATCH 请求。 ### ctx.http.ws(url) * **url:** `string` 请求地址 * 返回值: `WebSocket` 创建一个 WebSocket 连接。 --- --- url: /plugins/develop/mock.md --- # @koishijs/plugin-mock ::: tip 使用方法请参见 [开发 > 单元测试](../../cookbook/practice/testing.md) 章节。 ::: @koishijs/plugin-mock 包含了被 Koishi 使用的测试工具。它提供了一个名为 `mock` 的服务,可用于模拟事件上报、网络请求等等。 ## 类:Mock ### mock.webhook * 类型: [`Webhook`](#类-webhook) 用于模拟网络请求。 ### mock.client(userId, channelId?) * **userId:** `string` 用户 ID * **channelId:** `string` 频道 ID * 返回值: [`Client`](#类-client) 创建一个客户端。 ### mock.receive(event) * **event:** [`Event`](../../api/core/session.md#session-event) 事件体 * 返回值: `string` 触发会话事件。 ### mock.initUser(id, authority?, data?) * **id:** `string` 用户 ID * **authority:** `number` 权限等级 * **data:** `Partial` 其他用户数据 在数据库中初始化一个用户。等价于 `database.create('user', { mock: id, authority, ...data })`。 ### mock.initChannel(id, assignee?, data?) * **id:** `string` 频道 ID * **assignee:** `string` 频道代理人 * **data:** `Partial` 其他频道数据 在数据库中初始化一个频道。等价于 `database.create('channel', { platform: 'mock', id, assignee, ...data })`。 ## 类:Client **客户端 (Client)** 是对发往同一上下文的多次消息的一个抽象。它使用 `mock.client()` 方法创建,并借助 `mock.receive()` 实现其功能。 ::: warning 这个类下的大部分方法的返回都基于 [session.send](../../api/core/session.md#session-send) 方法和 [`middleware`](../../api/core/events.md#事件-middleware) 事件。在提供了极大方便的同时,会话也存在一些限制。如果你的插件存在以下几种特殊情况之一: * 使用了异步的 message 事件监听器 * 中间件和指令中可能存在未阻塞的异步操作 * 直接调用 Bot API 而非 session.send 这个类的方法可能会返回预料之外的结果。当然,如果要测试这些特殊情况,我们也有其他的解决方案。 ::: ### client.receive(content) * **content:** `string` 要发送的信息 * 返回值: `Promise` 收到的回复列表 模拟发送一条消息。 ### client.shouldReply(content, reply?) * **content:** `string` 要发送给机器人的信息 * **reply:** `string | RegExp | (string | RegExp)[]` 应有的回复,如果略去则不会进行比较 * 返回值: `Promise` 断言某条信息应存在某些回复。 ### client.shouldNotReply(content) * **content:** `string` 要发送给机器人的信息 * 返回值: `Promise` 断言某条信息不应存在任何回复。 ## 类:Webhook **网络钩子 (Webhook)** 可用于模拟到 Koishi 服务器的网络请求。 ### webhook.get(path, headers?) * **path:** `string` 请求路径 * **headers:** `object` 请求头 模拟 GET 请求。 ### webhook.post(path, body, headers?) * **path:** `string` 请求路径 * **body:** `string` 请求正文 * **headers:** `object` 请求头 模拟 POST 请求。 --- --- url: /plugins/develop/proxy-agent.md --- # @koishijs/plugin-proxy-agent @koishijs/plugin-proxy-agent 为 [`http`](./http.md) 服务提供了额外的网络代理能力,支持 HTTP(S), SOCKS 系列协议。 ## 配置项 ### config.proxyAgent * 类型: `string` 请求时默认使用的网络代理。 --- --- url: /plugins/develop/server.md --- # @koishijs/plugin-server @koishijs/plugin-server 提供了 `ctx.server` 基础服务,它是一个基于 [Koa Router](https://github.com/koajs/router) 的简单路由系统,用于管理 Koishi 应用收到的网络请求。除了 Koa Router 所支持的部分方法外,Router API 还提供了一些额外的功能,例如支持接受 WebSocket 连接等。 ## 上下文 API ::: warning 请避免使用未在本页列出的方法: 1. Koa Router 的 API 并不是副作用安全的。我们对本页列出的方法进行了特殊处理,使其在插件卸载时自动删除路由。但使用其他方法会导致你的插件无法被正常卸载。 2. 未来可能会不基于 Koa Router 重新实现路由系统。 ::: ### ctx.server\[method]\(path, middleware) * **method:** 可以是 `get`, `post`, `put`, `delete`, `patch` 或 `all` (仅能是小写) * **path:** `string | RegExp | (string | RegExp)[]` 路径 * **middleware:** `Function` Koa 中间件 处理特定路径上的网络请求。具体请参见 [这里](https://github.com/koajs/router/blob/master/API.md)。 ### ctx.server.ws(path, handler) * **path:** `string | RegExp | (string | RegExp)[]` 路径 * **handler:** `WebSocketHandler` 处理函数,接受下列参数 * **socket:** [`WebSocket`](https://github.com/websockets/ws/blob/master/doc/ws.md#class-websocket) WebSocket 连接 * **request:** [`IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage) 网络请求 在给定的路径上支持 WebSocket 连接。 ## 配置项 ### config.host * 类型:`string` * 默认值:`'127.0.0.1'` 服务器监听的 IP 地址。如果将此设置为 `0.0.0.0` 将监听所有地址,包括局域网和公网地址。 ### config.port * 类型:`number` * 必需选项 要监听的初始端口号。Koishi 建议使用 `5140` 作为默认端口。 ### config.maxPort * 类型:`number` * 默认值:[`config.port`](#config-port) 允许监听的最大端口号。插件启动时,如果设定的端口号被占用,插件会尝试自动切换到下一个端口,直到找到一个可用的端口,或是超出 `maxPort` 的限制。 ### config.selfUrl * 类型:`string` Koishi 服务暴露在公网的地址。部分插件 (例如 [adapter-telegram](../adapter/telegram.md) 或是 [assets-local](https://assets.koishi.chat/plugins/local.html)) 需要用到。 --- --- url: /plugins/develop/server-proxy.md --- # @koishijs/plugin-server-proxy @koishijs/plugin-server-proxy 实现了一个代理服务器。它可用于解决控制台跨域问题。 ## 配置项 ### config.path * 类型: `string` * 默认值: `'/proxy'` 服务器监听的路径。 --- --- url: /plugins/develop/server-satori.md --- # @koishijs/plugin-server-satori @koishijs/plugin-server-satori 实现了一个 [Satori 协议](https://satori.chat/) 的服务器。这个插件将允许你: * 通过 HTTP / WebSocket 访问当前 Koishi 实例中的机器人 * 配合 [Satori 适配器](../adapter/satori.md),在另一个 Koishi 实例中完全控制当前实例中的机器人 ## 配置项 ### config.path * 类型: `string` * 默认值: `''` 服务器监听的路径。 --- --- url: /plugins/develop/server-temp.md --- # @koishijs/plugin-server-temp ::: warning 使用此插件需要你将 Koishi [部署到公网](../../manual/recipe/server.md) (通常你还需要准备一个公网域名)。 ::: @koishijs/plugin-server-temp 实现了一个临时文件服务。 ## 配置项 ### config.path * 类型: `string` * 默认值: `'/temp'` 服务器监听的路径。 ### config.selfUrl * 类型: `string` * 默认值: [`ctx.server.config.selfUrl`](./server.md#config-selfurl) 服务暴露在公网的地址。 ### config.maxAge * 类型: `number` * 默认值: `Time.minute * 5` 文件的最大有效时间。 --- --- url: /api.md --- # API 参考 --- --- url: /schema/advanced/computed.md --- # Computed:条件求值 ::: tip 此类型只能在 Koishi 中使用。 ::: `Schema.computed()` 类型可用于合并多个类型。一种最常见的用法是将配置项分为多组显示。 ```ts export default Schema.object({ foo: Schema.computed(Number), }).description('配置项') ``` --- --- url: /schema/basic/date.md --- # Date 由于 Date 不便于序列化,我们提供了两套描述 Date 的方式: * 使用 Date 类型:输入字符串,输出 Date 实例 * 使用 String 类型与三种可选的 `role` 属性 其中,Date 类型与 `datetime` 的前端体验是完全一致的,唯一区别在于输出的格式不同。字符串额外多出 `date` 和 `time` 两种格式,用于表达纯日期和纯时间字符串。 ```ts export default Schema.object({ value: Schema.date(), datetime: Schema.string().role('datetime'), date: Schema.string().role('date'), time: Schema.string().role('time'), }) ``` --- --- url: /schema/advanced/dynamic.md --- # Dynamic:动态类型 ::: tip 此类型只能在 Koishi 中使用。 ::: ::: warning 此特性为实验性功能,未来可能会有改动。 ::: `Schema.dynamic()` 用于使用动态类型。例如某个服务需要在运行时才能获取某个配置项的可能取值,而基于此服务的其他插件的配置又需要从这些值中选择一个。这个时候,实现服务的插件可以使用 `ctx.schema.set()` 来定义动态的类型,使用服务的插件则可以使用 `Schema.dynamic()` 来引用该类型。 ```ts // 提供服务的插件 // getChoices() 返回一个数组,假设为 ['foo', 'bar'] ctx.schema.set('choices', Schema.union(getChoices())) ``` ```ts // 使用服务的插件 export default Schema.object({ value: Schema.dynamic('choices').description('选择一个值。'), }).description('配置项') ``` --- --- url: /schema/advanced/union-tagged-1.md --- # Intersect + Union:配置联动 1 一种比较复杂的场景是以对象的某个属性值确定对象的其他属性的类型。善用 Intersect 和 Union,我们就可以轻松实现表单项的联动效果!试着切换 `enabled` 的取值,并观察下方表单项的变化吧。 ```ts export default Schema.intersect([ Schema.object({ enabled: Schema.boolean().default(false), }).description('基础配置'), Schema.union([ Schema.object({ enabled: Schema.const(true).required(), foo: Schema.number().description('请输入一个数值。'), bar: Schema.string().description('请输入一个字符串。'), }), Schema.object({}), ]) ]) ``` ::: tip 请注意这个例子中对于 `default()` 和 `required()` 的使用。由于配置项默认情况下都是可选的,所以下方的 `enabled` 如果类型与上方的默认值不同,就必须加上 `required()`;反过来,如果相同,你就不应该加上 `required()` (你甚至可以缺省不写,这就是为什么最下面出现了一个空白的 `object({})`)。 ::: --- --- url: /schema/advanced/union-tagged-2.md --- # Intersect + Union:配置联动 2 一种比较复杂的场景是以对象的某个属性值确定对象的其他属性的类型。善用 Intersect 和 Union,我们就可以轻松实现表单项的联动效果!试着切换 `type` 的取值,并观察下方表单项的变化吧。 ```ts export default Schema.intersect([ Schema.object({ shared: Schema.string(), type: Schema.union(['foo', 'bar']).required(), }).description('基础配置'), Schema.union([ Schema.object({ type: Schema.const('foo').required(), value: Schema.number().default(114514), }).description('特殊配置 1'), Schema.object({ type: Schema.const('bar').required(), text: Schema.string(), }).description('特殊配置 2'), ]), ]) ``` ::: tip 请注意这个例子中对于 `default()` 和 `required()` 的使用。由于配置项默认情况下都是可选的,所以下方的 `type` 配置项如果类型与上方的默认值不同,就必须加上 `required()`。在这个例子中,`type` 本来就是一个必需属性,所以下方的每一个 `type` 都必须加上 `required()`。 ::: --- --- url: /schema/advanced/intersect.md --- # Intersect:分组 Intersect 类型可用于合并多个类型。一种最常见的用法是将配置项分为多组显示。 使用 `.collapse()` 可以将分组默认折叠为一个单独的配置项。 ```ts export default Schema.intersect([ Schema.object({ foo: Schema.number(), bar: Schema.string(), }).description('分组 1'), Schema.object({ baz: Schema.string(), qux: Schema.boolean(), }).description('分组 2'), ]) ``` --- --- url: /schema/advanced/transform.md --- # Transform:输入转换 Transform 用于定义一个转换类型,通常与 Union 一同使用。当输入满足一参数类型时,将调用二参数转换输入作为输出。此次转换将直接修改输入的对象,以确保类型满足输出类型。在网页表单中,将只会显示输出类型。 ```ts export default Schema.object({ value: Schema.union([ Schema.array(String), Schema.transform(String, value => [value]), ]).default([]), }) ``` --- --- url: /schema/advanced/union-select.md --- # Union:单选框 Union 描述了多个子类型的联合。它的最基础形式是从多个固定值中选择一个。这里的每一个字符串是 `Schema.const()` 的简写形式。如果每个可选值有较长的描述文本,你可以进一步将 `role` 设置为 `radio`,这样一来所有的选项将显示在下方而不是右侧。 ```ts export default Schema.object({ value1: Schema.union(['foo', 'bar', 'qux']), value2: Schema.union([ Schema.const('foo').description('选项 1'), Schema.const('bar').description('选项 2'), Schema.const('baz').description('选项 3'), ]).role('radio'), }) ``` --- --- url: /schema/advanced/union-arbitrary.md --- # Union:联合类型 Union 同样支持多种不同类型的联合。你需要给每个子类型提供一个 description,它们会作为表单中呈现的选项。 ```ts export default Schema.object({ value: Schema.union([ Schema.const().description('unset'), Schema.number().description('number'), Schema.string().description('string'), Schema.const(true).description('true'), Schema.const(false).description('false'), Schema.object({ foo: Schema.string(), bar: Schema.number(), }).description('object'), ]), }) ``` --- --- url: /releases/v4.1.md --- # v4.1 版本介绍 * [Roadmap](https://github.com/koishijs/koishi/issues/472) * [v4.0.1](https://github.com/koishijs/koishi/releases/tag/4.0.1) * [v4.1.0](https://github.com/koishijs/koishi/releases/tag/4.1.0) ## 插件与热重载优化 在这个版本中我们优化了插件的加载方式。一方面我们缓解了 service 监听器数量很容易超过限制导致内存泄漏警告的问题,另一方面热重载导致的内存泄漏和加载错误的问题也得到了修复。 除此以外,我们还要隆重介绍 Koishi v4 的热重载功能。它将拥有许多全新的特性: ### 细粒度的模块依赖分析 过去的热重载功能会将任何文件变动都扩展到根插件 (即配置文件中声明的插件),并重载扩散路径上的每一个文件。而新的依赖分析工具能够将文件变动引发的副作用具体计算到每一个模块,因此部分变动在到达某个子插件时就会停止扩散,从而在大型插件中获得更好的开发体验。 为了实现这种优化,我们假设每个插件模块的导出都是原子的。换言之,如果一个模块的导出恰好是正在运行的插件,那么这个插件将是模块导出的唯一用途,在插件被加载以外的任何地方不会用到这个模块的导出。对于正确使用模块化进行开发的项目,达成这个约定并不困难。事实上,插件如果有任何其他需要提供的功能,也都可以通过 Service API 实现。 如果某些插件实在难以满足这个约定,我们也提供了额外的方案。开发者要么不将插件作为模块的导出 (比如作为导出的一部分),要么在插件中手动声明 `sideEffect`。两种方法都能规避依赖分析工具的优化,从而实行保守的热重载策略。 ### 热重载与配置管理的联动 在过去,插件热重载与插件管理 (@koishijs/plugin-manager) 是作为独立的功能开发的。当它们同时启用时可能会遇到一些问题。当插件管理器修改了静态的配置文件 (例如 koishi.config.yml) 时,热重载机制可能检测不到变动;但当我们将静态配置文件纳入依赖分析,又会导致配置管理器明明只修改了一个插件的配置,却会导致整个应用被重新启动 (因为配置文件的依赖优先级是最高的)。 这次更新我们针对这个问题提供了统一的解决方案。首先插件管理器将只针对静态配置文件的场景生效,其次当启用了热重载功能时,一旦需要修改配置文件,插件管理器将不再触发插件变动,交由依赖分析工具处理改动过的配置文件。开发者得以同时使用插件管理器和热重载功能,获得更好的开发体验。 ### 更多细节优化 新版本中我们还支持了新的配置项 `config.watch.debounce`,用于控制热重载的触发频率。默认情况下文件变动 100ms 之后才会触发热重载,解决了强迫症程序员频繁保存带来的高频率热重载问题。以及我们还加入了对 json 配置文件的写入支持 (不过有了 yaml 真的会有人用 json 吗)。 --- --- url: /releases/v4.10.md --- # v4.10 版本介绍 * [Roadmap](https://github.com/koishijs/koishi/issues/839) * [v4.10.0](https://github.com/koishijs/koishi/releases/tag/4.10.0) * [v4.10.1](https://github.com/koishijs/koishi/releases/tag/4.10.1) * [v4.10.2](https://github.com/koishijs/koishi/releases/tag/4.10.2) * [v4.10.3](https://github.com/koishijs/koishi/releases/tag/4.10.3) * [v4.10.4](https://github.com/koishijs/koishi/releases/tag/4.10.4) * [v4.10.5](https://github.com/koishijs/koishi/releases/tag/4.10.5) * [v4.10.6](https://github.com/koishijs/koishi/releases/tag/4.10.6) * [v4.10.7](https://github.com/koishijs/koishi/releases/tag/4.10.7) ## ORM 更新 此版本中,我们对 Koishi 背后的 ORM 框架进行了优化,极大地扩展了数据库 API 的表达能力: * 使用 WASM 重新实现了 @koishijs/plugin-database-sqlite,从而规避了用户安装失败的问题 * 支持了所有数据库统一的 `autoInc` 行为 * `sel.orderBy()` 和 `sel.project()` 支持了传入表达式 * 全面支持了 `sel.groupBy()` 的使用以及上述语法的多次链式调用 ## 消息组件 本次更新我们为消息元素加入了更多特性,包括组件、插值、注释、模板元素、JSX 支持等,同时也支持了消息组件在会话和本地化文件中的使用。这意味着用户可以直接在插件中编写 JSX 来发送消息,而不再需要使用 XML 字符串了。 ## 飞书适配器 经过了长达 7 个月的努力,我们终于完成了飞书适配器,让我们感谢 [@MaikoTan](https://github.com/maikotan)! ## 本地化编辑 发布了全新的 @koishijs/plugin-locales 插件,它提供了一个控制台页面,允许用户在线编辑任意本地化文本。 ## 配置文件插值 在这个版本中,我们还支持了使用插值语法配置非字符串的配置项。这允许你更好地利用环境变量进行配置,比如将 `port` 设置为 `${{ +env.BASE_PORT }}` 等等。这也意味着你现在甚至可以在配置文件中使用函数了! --- --- url: /releases/v4.11.md --- # v4.11 版本介绍 * [Roadmap](https://github.com/koishijs/koishi/issues/892) * [v4.11.0](https://github.com/koishijs/koishi/releases/tag/4.11.0) * [v4.11.1](https://github.com/koishijs/koishi/releases/tag/4.11.1) * [v4.11.2](https://github.com/koishijs/koishi/releases/tag/4.11.2) * [v4.11.3](https://github.com/koishijs/koishi/releases/tag/4.11.3) * [v4.11.4](https://github.com/koishijs/koishi/releases/tag/4.11.4) * [v4.11.5](https://github.com/koishijs/koishi/releases/tag/4.11.5) * [v4.11.6](https://github.com/koishijs/koishi/releases/tag/4.11.6) * [v4.11.7](https://github.com/koishijs/koishi/releases/tag/4.11.7) * [v4.11.8](https://github.com/koishijs/koishi/releases/tag/4.11.8) * [v4.11.9](https://github.com/koishijs/koishi/releases/tag/4.11.9) ## 过滤器与动态配置 在这个版本中,我们首先支持了使用 minato 的表达式语法作为 `$filter` 过滤会话上下文。在此基础上我们又提供了 `Schema.computed()` 类型,它可用于定义根据会话动态取值的配置项。这两个特性意味着用户真正获得了根据会话细粒度控制插件的能力。 ## 指令管理器 发布了全新的 @koishijs/plugin-commands 插件,它提供了一个控制台页面,允许用户在线管理指令的各项配置和层级关系。得益于过滤器机制,现在指令也可以在不同的平台和群组内拥有不同的行为了。 ## 插件市场优化 这个版本中我们对 @koishijs/plugin-market 的功能做了全面的优化: * 现在能显示 `peerDependencies` 是否可选了 * 当打开插件选择对话框时自动聚焦输入框 * 支持了导出 `usage` 以提供插件的配置说明 * 优化了搜索框下方插件数量的显示 * 支持了更多标识和搜索语法,包括不安全、预览版本和近期新增 * 插件配置界面顶部增加了了链接导航栏 * 当更新插件主版本时将显示兼容性提示 * 不再显示「所有插件」分组,优化了插件配置体验 * 当移除插件或插件组时将弹窗进行确认,同时禁止停用部分核心插件 * 当检测到新的连接时,根据上一次刷新的时间自动刷新插件市场 * 新增了左侧栏用于过滤和排序功能,并全面完善了分类系统 * 优化了插件卡片的悬浮和点击效果 * 修复了十余个 bug,这里就不一一介绍了 ## 状态栏更新 此外,我们还将 status 插件拆分为 status 和 analytics 两个插件,后者承担了数据统计和图标渲染的功能。对于前者,我们还引入了 envinfo 插件的功能:在控制台的左下角显示环境信息和版本号。在 status 插件之外,我们还内置了前端插件加载的进度条,已获得更好的初始加载体验。 --- --- url: /releases/v4.12.md --- # v4.12 版本介绍 * [Roadmap](https://github.com/koishijs/koishi/issues/1000) * [v4.12.0](https://github.com/koishijs/koishi/releases/tag/4.12.0) * [v4.12.1](https://github.com/koishijs/koishi/releases/tag/4.12.1) * [v4.12.2](https://github.com/koishijs/koishi/releases/tag/4.12.2) * [v4.12.3](https://github.com/koishijs/koishi/releases/tag/4.12.3) * [v4.12.4](https://github.com/koishijs/koishi/releases/tag/4.12.4) * [v4.12.5](https://github.com/koishijs/koishi/releases/tag/4.12.5) * [v4.12.6](https://github.com/koishijs/koishi/releases/tag/4.12.6) * [v4.12.7](https://github.com/koishijs/koishi/releases/tag/4.12.7) * [v4.12.8](https://github.com/koishijs/koishi/releases/tag/4.12.8) * [v4.12.9](https://github.com/koishijs/koishi/releases/tag/4.12.9) ## 用户绑定 在这个版本中,我们引入了新的内置数据表 `binding`,用于存储账号绑定信息。在新的架构下,我们更新了 `auth` 和 `bind` 插件,支持了同平台的用户绑定和解绑等功能。 为了能让旧版本的用户平滑升级,我们还引入了数据库迁移技术,支持插件在升级时将已有的数据迁移到新的表中。 ## 资源管理器 新增了官方插件 @koishijs/plugin-explorer,可用于在控制台中查看和编辑实例目录内的文件。该插件除了能够方便云端部署的用户进行文件管理外,还为路径类型的配置项提供了便捷的选择界面。至此,Koishi 的控制台生态已经趋于完善。 ## 配置界面优化 v4.12 版本期间,我们对配置 UI 库 schemastery-vue 进行了全面的重构: * 提供了更好的扩展性 * 修复了一些边界情况下的已知问题,同时提高了性能 * 对于 `array` 和 `dict` 等类型支持了折叠 * 支持了配置项国际化 ## 适配器更新 * 优化了多个适配器的消息元素支持,包括 Telegram, KOOK 等 * 将 Feishu 适配器更名为 Lark,并同时支持了飞书的国内与国际版本 * 新增了 Mail 适配器,允许用户通过邮件与机器人交互 ## 热重载优化 将热重载相关逻辑从 CLI 内置迁移到了独立的插件 @koishijs/plugin-hmr 中,并提供了更多的配置项与功能。例如,如果当前保存的文件存在编译错误,将会显示色彩丰富的提示信息。 --- --- url: /releases/v4.13.md --- # v4.13 版本介绍 * [Roadmap](https://github.com/koishijs/koishi/issues/1085) * [v4.13.0](https://github.com/koishijs/koishi/releases/tag/4.13.0) * [v4.13.1](https://github.com/koishijs/koishi/releases/tag/4.13.1) * [v4.13.2](https://github.com/koishijs/koishi/releases/tag/4.13.2) * [v4.13.3](https://github.com/koishijs/koishi/releases/tag/4.13.3) * [v4.13.4](https://github.com/koishijs/koishi/releases/tag/4.13.4) * [v4.13.5](https://github.com/koishijs/koishi/releases/tag/4.13.5) * [v4.13.6](https://github.com/koishijs/koishi/releases/tag/4.13.6) * [v4.13.7](https://github.com/koishijs/koishi/releases/tag/4.13.7) * [v4.13.8](https://github.com/koishijs/koishi/releases/tag/4.13.8) * [v4.13.9](https://github.com/koishijs/koishi/releases/tag/4.13.9) ## 控制台优化 v4.13 版本的主要改动是新增了一批控制台 API: * 引入了主题系统,允许插件定义新的主题 * 引入了用户配置 API,并允许插件扩展配置项 * 引入了菜单 API,允许插件定义和触发上下文菜单 * 支持了配置漫游功能,用户可以在多个客户端同步配置 伴随着新的 API,已有的控制台插件也迎来了各种优化,这里就不一一列举了。 ## 插件配置优化 在这个版本中,我们进一步将 @koishijs/plugin-market 拆分为了 market 和 config 两个独立的插件。前者负责插件的安装和更新,后者负责插件的配置。同时,我们支持了插件信息的按需读取,大幅提高了启动速度和插件市场加载速度。 新版本的 market 和 config 插件也带来了一些新功能: * 支持了启用、停用、重载插件分组 * 支持了插件的快速预览功能 * 支持了对插件配置项的热重载 * 支持了以指令形式安装、卸载、更新插件 ## 适配器更新 * 支持了更多标准消息元素 * 支持了斜线指令,允许用户快速与机器人交互 * 新增了与表态和角色相关的 Bot API * 新增了 matrix 和 line 适配器 --- --- url: /releases/v4.14.md --- # v4.14 版本介绍 * [Roadmap](https://github.com/koishijs/koishi/issues/1138) * [v4.14.0](https://github.com/koishijs/koishi/releases/tag/4.14.0) * [v4.14.1](https://github.com/koishijs/koishi/releases/tag/4.14.1) * [v4.14.2](https://github.com/koishijs/koishi/releases/tag/4.14.2) * [v4.14.3](https://github.com/koishijs/koishi/releases/tag/4.14.3) * [v4.14.4](https://github.com/koishijs/koishi/releases/tag/4.14.4) * [v4.14.5](https://github.com/koishijs/koishi/releases/tag/4.14.5) * [v4.14.6](https://github.com/koishijs/koishi/releases/tag/4.14.6) ## 权限系统 v4.14 版本中,我们引入了新的权限系统。相比过去用数字来表示用户的权限等级,新的权限系统将提供一套更加灵活的权限管理方案,包括: * 支持细粒度的权限控制 * 支持权限组和用户组等概念 * 允许插件自定义权限 * 能够与平台原生的权限进行对接 新的权限系统未来还会与控制台、sudo 插件等进行整合,将权限管理的边界扩展到更多的场景。 ## 配置界面优化 配置项库 schemastery-vue 升级到了 v7 版本,包含以下更新: * 重新设计了 UI,将菜单置于配置项右侧按钮处,并为菜单项增加了图标 * 支持了 `transform` 类型中回调函数的序列化 * 支持了 `.collapse()` 方法,允许显式声明一个配置项可折叠 * 支持了 `.experimental()` 和 `.deprecated()` 方法,用于声明配置项的实验性或已废弃状态 * 部分类型支持了「在上方插入」「在下方插入」菜单项 * `bitset` 类型支持了「全部选中」「清空选择」菜单项 * 支持了 `.extra()` 方法和类型扩展 ## 适配器更新 这个版本中我们增加了大量官方适配器,可用于以下平台: * 钉钉 * 微信公众号 * 企业微信 * Slack * WhatsApp 同时也为现有的适配器增加了新的功能: * 支持了斜线指令 (Discord, Telegram) ## 协议升级 最后,我们也将 Satori 更新到了 v3 alpha 版本。新版本引入了一些实验性功能,并将在 Koishi v4.15 中进入稳定版本。关于这些新功能就留到下个版本介绍吧。 --- --- url: /releases/v4.15.md --- # v4.15 版本介绍 * [Roadmap](https://github.com/koishijs/koishi/issues/1208) * [v4.15.0](https://github.com/koishijs/koishi/releases/tag/4.15.0) * [v4.15.1](https://github.com/koishijs/koishi/releases/tag/4.15.1) * [v4.15.2](https://github.com/koishijs/koishi/releases/tag/4.15.2) * [v4.15.3](https://github.com/koishijs/koishi/releases/tag/4.15.3) * [v4.15.4](https://github.com/koishijs/koishi/releases/tag/4.15.4) * [v4.15.5](https://github.com/koishijs/koishi/releases/tag/4.15.5) * [v4.15.6](https://github.com/koishijs/koishi/releases/tag/4.15.6) ## Satori v3 v4.15 版本最重要的特性就是将 Satori 更新到了 v3 版本。新版本对现有 API 进行了一些重构,并从中总结出了一个通用的跨平台聊天协议,名为 Satori Protocol。你可以在 [Satori 官网](https://satori.chat/) 上查看完整的协议文档。与此同时,我们也发布了一些与适配器与网络相关的插件: * [adapter-satori](../plugins/adapter/satori.md):适用于 Satori 协议的适配器 * [server-satori](../plugins/develop/server-satori.md):支持 Satori 协议的 API 服务器 * [server-proxy](../plugins/develop/server-proxy.md):代理服务器,可用于解决前端跨域问题 * [server-temp](../plugins/develop/server-temp.md):临时服务器,可用于本地图片发送等场景 这些插件同步发布在 Satori 和 Koishi 侧。为此,我们发布了 [upstream](https://github.com/koishijs/upstream) 仓库,用于同步上游生态中的插件。未来我们将提供让 Koishi 能够直接加载上游插件的能力,敬请期待。 此外,Satori v3 也引入了更多原生交互能力,包括按钮元素、交互事件等。相关的能力已经在全部支持按钮的平台 (KOOK, Discord, LINE, QQ, Telegram, WhatsApp) 上实现。 ## Cordis v3 另一个重要程度不亚于 Satori v3 的更新则是 Cordis v3。新版本基于 `Proxy` 重新实现了整套的服务 API,相比旧版本具有更强的可追溯性、可靠性和扩展性。同时,新版本的所有 API 都是无副作用的,这意味着困扰用户的多例问题将大幅减少。 由于这些优化处于底层,对于开发者比较难以察觉,因此这里举出一个直接的应用。在插件内部使用 `ctx.logger()` 发送日志时,将基于上下文进行追踪,在控制台的插件详情页面中将展示该插件产生的日志。同时,现有的日志界面也支持了对日志进行跳转,以便于开发者快速定位问题。 ## 数据库更新 在这个版本中,我们还对 ORM 能力进行了大幅优化: * 修复了 `join`, `groupBy`, `project` 在若干边界情况下的问题 * 支持了 `$.array()` 和 `$.object()` 等用于操作结构化数据的 API * 对数据库进行 `set`, `upsert`, `remove` 操作时将返回修改条目相关信息了 * 修复了 SQL 对 JSON 中嵌套属性访问异常的问题 * 优化了 SQLite 的访问性能,解决了数据库体积过大时无法使用的问题 * 新增了 PostgreSQL 数据库支持 ## 控制台更新 这个版本也优化了控制台的使用体验: * 使用统一的菜单 API 显示布局菜单和上下文菜单,为多个插件提供了更多的菜单项 * 支持了快捷键绑定 (例如,在插件配置界面键入 ⌘S 将会保存配置) * config 插件支持了插件和插件组在侧边栏的重命名 * config 插件支持在启用、重载插件时将对配置项进行检查 * explorer 插件支持了符号链接的解析与显示 * 优化了可访问性,修复了若干无法显示某些文本的问题 ## QQ 适配器 随着腾讯开放平台的的推出,开发者现在可以直接接入官方机器人 API,而无需使用第三方登录方案。因此,我们发布了 QQ 适配器 (能够同时支持 QQ 群和 QQ 频道机器人的使用),同时废弃了过时的 QQGuild 适配器。此外 OneBot 适配器也转移到社区维护。 --- --- url: /releases/v4.2.md --- # v4.2 版本介绍 * [Roadmap](https://github.com/koishijs/koishi/issues/482) * [v4.1.1](https://github.com/koishijs/koishi/releases/tag/4.1.1) * [v4.1.2](https://github.com/koishijs/koishi/releases/tag/4.1.2) * [v4.2.0](https://github.com/koishijs/koishi/releases/tag/4.2.0) ## 指令系统优化 ### Git 风格的指令调用 Koishi 将子指令分为两类:**层级式** 和 **派生式**。前者从形式上将指令组织起来方便查看和管理,而后者则从行为上限制了指定的调用方式。说白了层级式子指令 `foo/bar` 仍然可以使用 `bar` 进行调用,而派生式子指令 `foo.bar` 则必须完整写全整个指令路径。 从这个版本起我们内置支持了 Git 风格的指令调用,即当父指令 `foo` 没有实现时,`foo bar` 将自动视为 `foo.bar` 的调用,其参数和选项也会被保留。 ### @koishijs/plugin-forward 作为官方的第一个实例,最新版本的 @koishijs/plugin-forward 提供了在运行时通过指令查看、添加、删除转发规则的功能: ```js forward add #123 // 添加目标频道 forward list // 查看全部目标频道 fwd clear // 移除全部目标频道 fwd rm #123 // 移除特定目标频道 ``` 无论是父指令还是子指令,我们都支持使用别名书写。在上面的例子里,`fwd rm` 与 `forward remove` 的效果是完全一致的。 ## 控制台优化 我们引入了全新的客户端 API,以实现控制台插件在客户端的可插拔性。这些改动也让我们得以提供全面的控制台体验。 ### 更多的控制台插件 在这个版本中我们优化了控制台系统,并将部分功能单独发布为新的插件。它们包括: * [@koishijs/plugin-dataview](https://github.com/koishijs/koishi-plugin-dataview) (来自 status 插件) * [@koishijs/plugin-insight](../plugins/console/insight.md) (来自 manager 插件) * [@koishijs/plugin-logger](../plugins/console/logger.md) (来自 status 插件) ### @koishijs/plugin-manager @koishijs/plugin-manager 作为新人可能接触到的第一个插件,我们也大幅优化了它的能力,包括: * 优化了暗色主题的配色方案 * 表单支持了更多类型的 Schemastery 类型,基本完成了对常见表单类型的覆盖 * 重构了插件市场的界面,使用卡片进行展示,并辅以搜索和标签功能 * 新增了依赖管理界面,允许批量安装 / 更新 / 删除依赖 * 新增了机器人配置页面,支持了登录 / 下线 / 移除等操作 --- --- url: /releases/v4.3.md --- # v4.3 版本介绍 * [Roadmap](https://github.com/koishijs/koishi/issues/501) * [v4.2.1](https://github.com/koishijs/koishi/releases/tag/4.2.1) * [v4.2.2](https://github.com/koishijs/koishi/releases/tag/4.2.2) * [v4.3.0](https://github.com/koishijs/koishi/releases/tag/4.3.0) * [v4.3.1](https://github.com/koishijs/koishi/releases/tag/4.3.1) ## 更好的群组支持 部分插件需要访问群组数据而非频道数据 (例如 status 插件的发言统计功能)。对于 Telegram 这类群组与频道统一的平台,我们只需要访问 `session.channel` 就可以,但是对于 Discord 这类群组与频道分离的平台,过去并没有很好的处理群组数据的办法。为此,我们引入了 `session.guild` 属性。 群组数据采用 Channel 的数据结构,并且也存储于同一张表中。我们为每一个 Channel 添加 `guildId` 属性,表示一个频道所在的群组。如果一个 Channel 实例的 `guildId` 属性等于自身 `id`,则其视为一个群组。如果某个平台的群组和频道概念重合,则对应的 `session.guild` 与 `session.channel` 一致。 这种做法不仅解决了上文中所述的问题,实际上也修复了部分平台发送广播失败的问题。在 QQ 频道中,发送频道消息不仅需要提供频道 ID,也需要同时提供群组 ID。在过去的版本中,当发送广播时无法获得一个频道所在的群组,因此发送会失败。经过这次的更新,此问题也被成功解决。 ## 全新的控制台插件 在这个版本中,我们同样迎来了两个全新的控制台插件。 ### @koishijs/plugin-auth 用过 v3 控制台的用户一定熟悉账户功能。用户可以以密码或者验证码两种方式登录,并且所有的控制台功能都会受到权限等级的限制。虽然这种方式为可以让更多用户安全地接触到控制台,但是本身也为控制台的使用者带来了一定的门槛。这是因为,要使用账户系统,就意味着用户已经安装了数据库服务,并且将自己设置为了管理员。然而 v4 中的控制台被设计成许多核心功能的桥梁,自然不应该有各种限制。出于上述考虑,v4 的控制台默认情况下是没有账户系统的。 但是我们也必须承认,账户功能和权限管理对于想要公开展示控制台的人来说是非常必要的功能。因此我们在新版本中也提供了 @koishijs/plugin-auth 插件,它作为一个控制台扩展支持了这套账户系统。安装了该插件的用户可以放心地将自己的控制台发布在公网中。 ### @koishijs/plugin-sandbox 与 auth 插件类似,近期加入的 sandbox 插件也对应着 v3 控制台中的内置功能。它提供了一个虚拟平台,使用者可以在这个平台中创建账户与机器人对话,以此调试机器人的各项功能。不过在 v3 的基础上,我们加入了大量的新特性,不仅允许同时创建多个账户,也支持在群聊模式下进行对话。可以说基本覆盖了绝大部分被动型插件的使用场景。 v4 的新用户可能会对这个插件的功能感到有些迷惑,因为似乎已经有与这个插件类似的官方插件存在了。这里我们正好做一个对比: * sandbox 与 chat 的区别: * 前者提供一个虚拟平台,使用者扮演平台用户与机器人对话 * 后者则在真实平台中,使用者扮演的是实际的机器人账号 * sandbox 与 mock 的区别: * 前者是一个控制台插件,适合用 GUI 快速调试插件的功能,只适合调试被动型插件 * 后者则更加强大和复杂,所有场景都能够胜任,一般结合单元测试框架在 CI 中使用 --- --- url: /releases/v4.4.md --- # v4.4 版本介绍 * [Roadmap](https://github.com/koishijs/koishi/issues/522) * [v4.3.2](https://github.com/koishijs/koishi/releases/tag/4.3.2) * [v4.3.3](https://github.com/koishijs/koishi/releases/tag/4.3.3) * [v4.4.0](https://github.com/koishijs/koishi/releases/tag/4.4.0) ## 命令行工具链 v4.3 到 v4.4 期间我们完善了整套命令行工具链,它们包括: * create-koishi: 快速搭建模板项目,包含 Koishi 的大部分特性 * @koishijs/cli: Koishi 启动工具,提供了监视进程、热重载、环境变量等功能 * @koishijs/scripts: 开发环境工具包,为插件开发与发布提供了一套便利指令 * @koishijs/bootstrap: 快速创建生产环境,适用于 Docker 等云端部署场景 参见:[工作区开发](../guide/develop/workspace.md) ## Schema 能力提升 同时,在近期的版本我们也大幅提升了 Schema 在网页控制台的表达能力。 * 现在网页控制台的配置面板中已经可以正常显示 array, dict 和 object 类型的任意多级嵌套了 * 我们还支持了 Schema 的递归表达,并且确保递归表达的 Schema 也能正常序列化和反序列化 参见:[配置构型](../guide/plugin/schema.md) --- --- url: /releases/v4.5.md --- # v4.5 版本介绍 * [Roadmap](https://github.com/koishijs/koishi/issues/552) * [v4.4.1](https://github.com/koishijs/koishi/releases/tag/4.4.1) * [v4.4.2](https://github.com/koishijs/koishi/releases/tag/4.4.2) * [v4.5.0](https://github.com/koishijs/koishi/releases/tag/4.5.0) ## 多语言支持 如果你在运营一个大型社区,那么你可能会遇到这种场景:群组内设立了许多不同语言的频道,每个频道分别供不同地区的用户进行交流。在这种情况下,最合适的做法是让你的机器人在不同的频道下使用不同的语言进行回复。本质上,这不会改变机器人的运行逻辑,因此最好的做法是将涉及的每一段文本都抽离出来,通过统一的方式进行管理,并在发送前进行本地化渲染。 为此,Koishi 提供了一套新的 API:你可以使用 `ctx.i18n.define()` 定义多语言翻译,并通过 `session.text()` 渲染本地化文本。这套系统默认会依次读取频道语言、群组语言、用户语言和默认配置,并执行一套回退机制。 在渲染方面,这套系统不仅将继承过去 Template API 的插值语法,而且在此基础上加入了渲染预设机制,能够帮助处理单复数、列表渲染、随机渲染等更多复杂场景。 为了更好地实现代码解耦,我们还加入了本地翻译文件的支持。你可以创建一个 locales 目录并在其中按语言名称建立相应的文件,随后只需在插件中引入他们即可。同时,我们也提供了一套多语言指令帮助的编写规范,这样一来不仅所有的翻译文本都不需要写在源代码中,而且用户也可以更加方便地覆盖插件内置的文本。 参见:[多语言支持](../guide/i18n/index.md) ## 数据模型支持嵌套属性 许多插件都会通过扩展数据模型的方式来实现自己所需的功能。除了创建新的表外,一部分插件也会选择扩展已有数据表中的字段。如果插件提供了多个字段,就需要考虑扩展字段的冲突问题。在过去的版本中,插件开发者只能通过将这些字段名起长一些来规避冲突,但这同样也会让开发变得有些麻烦。 在这次的新版本中,我们支持了数据模型的嵌套属性来解决这个问题。下面是一个简单的例子: ```ts ctx.model.extend('user', { 'github.accessToken': 'string', 'github.refreshToken': 'string', }) const user = await ctx.database.get('user', ['github']) user.github.accessToken // string ``` 可以看到,我们在 `user` 数据模型中扩展了两个字段,它们都有一个公共的 `github.` 前缀。尽管它们在存储上依旧可以有独立的类型和默认值,但是开发者只需通过一个简单的 github 属性便可访问它们。 --- --- url: /releases/v4.6.md --- # v4.6 版本介绍 * [Roadmap (Part 1)](https://github.com/koishijs/koishi/issues/578) * [v4.5.1](https://github.com/koishijs/koishi/releases/tag/4.5.1) * [v4.5.2](https://github.com/koishijs/koishi/releases/tag/4.5.2) * [v4.6.0](https://github.com/koishijs/koishi/releases/tag/4.6.0) * [Roadmap (Part 2)](https://github.com/koishijs/koishi/issues/615) * [v4.6.1](https://github.com/koishijs/koishi/releases/tag/4.6.1) * [v4.6.2](https://github.com/koishijs/koishi/releases/tag/4.6.2) * [v4.6.3](https://github.com/koishijs/koishi/releases/tag/4.6.3) ## 版本规划调整 如你所见,我们调整了 minor 版本的规划。在此之前,每个 minor 版本的规划是从前一个 minor 版本发布后开始计算的;而从 v4.6 开始,每个 minor 版本的规划将会计算到下一个 minor 版本发布之前。同时,未来的每个 minor 版本也将会包含更多的 patch 版本,每个 patch 版本的发布周期暂定为一周。 ## 更强大的 ORM 在这个版本中,我们将 ORM 相关功能移至了一个新仓库 cosmotype (后更名为 minato) 中。在完成与 Koishi 的解耦的同时,我们也引入了全新的 Selection API 和 Evaluation Callback。这些新功能将允许包括连接在内的更多高级数据库操作。 与此同时,我们也增加和细化了许多单元测试,并完善了初始化和空值判断相关的各项功能。 参见:[Query Builder Roadmap](https://github.com/koishijs/koishi/issues/595) ## 功能解耦 在两年多的开发过程中,Koishi 已经成长为一个综合性框架了,其中的许多功能完全可以独立成库。而实际上 Koishi 中的很多模块都可以或已经被用于其他项目中。正是考虑到这些情况,除了 ORM 以外,我们也解耦了其他一些功能,例如 [reggol](https://github.com/shigma/reggol),[ns-require](https://github.com/shigma/ns-require) 和 [yakumo](https://github.com/shigma/yakumo) 等。在未来的版本中,我们也将对 Koishi 的核心功能进行拆分,以便满足更多开发者的需求,敬请期待。 除此以外,部分大型官方插件也被拆分至独立的仓库,例如 [eval](https://github.com/koishijs/koishi-plugin-eval), [github](https://github.com/koishijs/koishi-plugin-github) 和 [teach](https://github.com/koishijs/koishi-plugin-dialogue) 等。 --- --- url: /releases/v4.7.md --- # v4.7 版本介绍 * [Roadmap](https://github.com/koishijs/koishi/issues/662) * [v4.7.0](https://github.com/koishijs/koishi/releases/tag/4.7.0) * [v4.7.1](https://github.com/koishijs/koishi/releases/tag/4.7.1) * [v4.7.2](https://github.com/koishijs/koishi/releases/tag/4.7.2) * [v4.7.3](https://github.com/koishijs/koishi/releases/tag/4.7.3) * [v4.7.4](https://github.com/koishijs/koishi/releases/tag/4.7.4) * [v4.7.5](https://github.com/koishijs/koishi/releases/tag/4.7.5) * [v4.7.6](https://github.com/koishijs/koishi/releases/tag/4.7.6) ## 可重用插件 在这个版本中,我们将 Koishi 的核心框架迁命名为 [Cordis](https://github.com/shigma/cordis),并迁移到了新的仓库。这个框架包含了 Koishi 的诸多核心功能,包括上下文、插件系统、事件模型等等。同时,我们也提供了一套方案用于解决插件的多次加载问题。 在过去,一个插件同时只有一次加载是有效的。当一个插件已处于运行状态时,再次加载将没有任何效果。然而部分插件确实存在多次加载的需求,比如适配器插件。为此,我们提出了插件作用域的概念。每个上下文都有自己所绑定的作用域,当作用域被卸载时,对应的上下文上的副作用也会被一并清除。同时,我们引入了一个新的生命周期事件 `fork`。它将在一个插件每一次加载时出发。由此便可以实现插件的重用,同时无需担心影响过去的插件行为。 ## 新版插件市场 与此同时,我们也编写了全新的 market 和 insight 插件,提供了更好的插件安装和配置体验。 首先,与过去扁平化的管理机制不同,我们推出了插件组的概念。在配置界面中,我们也使用树形结构显示每一个插件和插件组。用户可以根据自己的需要对插件组进行更名、折叠和展开。 在依赖图方面,我们也重构了 UI,改为使用力导向图展示插件之间的依赖关系。 此外,我们允许插件开发在在 `package.json` 中提供 `koishi` 属性以声明插件的元信息。这些信息将被插件市场自动读取,并提供更好的安装提示。 --- --- url: /releases/v4.8.md --- # v4.8 版本介绍 * [Roadmap](https://github.com/koishijs/koishi/issues/725) * [v4.8.0](https://github.com/koishijs/koishi/releases/tag/4.8.0) * [v4.8.1](https://github.com/koishijs/koishi/releases/tag/4.8.1) * [v4.8.2](https://github.com/koishijs/koishi/releases/tag/4.8.2) * [v4.8.3](https://github.com/koishijs/koishi/releases/tag/4.8.3) * [v4.8.4](https://github.com/koishijs/koishi/releases/tag/4.8.4) * [v4.8.5](https://github.com/koishijs/koishi/releases/tag/4.8.5) * [v4.8.6](https://github.com/koishijs/koishi/releases/tag/4.8.6) * [v4.8.7](https://github.com/koishijs/koishi/releases/tag/4.8.7) ## 通用适配器协议 这个版本中,我们引入了全新的通用聊天协议 Satori,并将现有的适配器插件迁移到了新的组织下。与现有的通用聊天协议相比,Satori 有以下优势: * 抛弃历史包袱,设计了更加通用的接口,并不与任何特定平台绑定 * 将自身定位为聊天协议而非机器人框架,因而更加轻量,也有更多的应用场景 同时,我们也调整了适配器 / 机器人的 API,使每一个适配器插件都成为可重用的插件,再次优化了交互体验。 ## 全新的控制台界面 我们为控制台相关插件创建了一个新仓库 [koishijs/webui](https://github.com/koishijs/webui) 中,并且为这些插件都发布了新的大版本,主要包含以下变更: * 设计了全新的 UI,能够更好地承载多样化的功能,并且更加适配原生应用 * 新增了屏幕底部的状态栏,可用于显示登录信息、负载状态、版本信息等 * 提供了专门的欢迎界面,并为大部分功能提供了移动端适配 --- --- url: /releases/v4.9.md --- # v4.9 版本介绍 * [Roadmap](https://github.com/koishijs/koishi/issues/790) * [v4.9.0](https://github.com/koishijs/koishi/releases/tag/4.9.0) * [v4.9.1](https://github.com/koishijs/koishi/releases/tag/4.9.1) * [v4.9.2](https://github.com/koishijs/koishi/releases/tag/4.9.2) * [v4.9.3](https://github.com/koishijs/koishi/releases/tag/4.9.3) * [v4.9.4](https://github.com/koishijs/koishi/releases/tag/4.9.4) * [v4.9.5](https://github.com/koishijs/koishi/releases/tag/4.9.5) * [v4.9.6](https://github.com/koishijs/koishi/releases/tag/4.9.6) * [v4.9.7](https://github.com/koishijs/koishi/releases/tag/4.9.7) ## 消息元素 在这个版本中,我们引入了消息元素,作为 Satori 的新的消息格式。消息元素是拥有类似 XML 的结构,由标签、属性和内容组成。与过去所使用的 CQ 码相比,消息元素有以下优势: * 更加通用,不与任何特定平台耦合,编码规范也与主流实践相符 * 可以表达多层的结构,从而适配了 Matrix 等支持富文本的平台 * 具有更强的扩展性,未来还可以支持模板渲染等高级特性 ## 细粒度的热重载 我们还引入了更细粒度的热重载功能:当你在运行时通过配置文件或控制台,修改某个插件或全局的部分配置时,会根据这个配置的具体功能决定是否需要重载插件或整个应用。开发者可以声明自己插件的具体哪些配置项应该触发重载,以及不触发重载的配置项被修改时的回调函数。基于这个特性,我们也大幅优化了 @koishijs/loader 的加载逻辑,使得插件组本身也成为了真正的插件。 ## 用户登录优化 这个版本也同样迎来了 auth 插件的一些更新: * 优化了个人信息界面,支持了跨平台用户绑定功能 * 支持自动创建初始管理员用户,一定程度上避免了盲目启用插件而导致无法登录的问题 --- --- url: /api/core/context.md --- # 上下文 (Context) **上下文 (Context)** 是 Koishi 的核心概念。Koishi 的绝大多数功能也直接通过上下文提供,包括插件、中间件、监听器和指令等。 ## 服务与混入 Koishi 使用了组合 (Compose) 的开发方式,绝大部分上下文属性和方法都通过混入的方式搭载在了服务上。以下的属性和方法是由内置服务提供的,你可以像使用实例属性和方法一样使用它们。这些 API 的具体用法在服务文档中详细介绍,你可以点击对应的链接前往查看。 * [ctx.any](../service/filter.md#ctx-any) * [ctx.bail](../service/events.md#ctx-bail) * [ctx.before](../service/events.md#ctx-before) * [ctx.debounce](../service/timer.md#ctx-debounce) * [ctx.emit](../service/events.md#ctx-emit) * [ctx.events](../service/events.md) * [ctx.exclude](../service/filter.md#ctx-exclude) * [ctx.filter](../service/filter.md#ctx-filter) * [ctx.http](../service/http.md) * [ctx.i18n](../service/i18n.md) * [ctx.inject](../service/registry.md#ctx-inject) * [ctx.intersect](../service/filter.md#ctx-intersect) * [ctx.loader](../service/loader.md) * [ctx.middleware](../service/events.md#ctx-middleware) * [ctx.model](../database/model.md) * [ctx.never](../service/filter.md#ctx-never) * [ctx.off](../service/events.md#ctx-off) * [ctx.on](../service/events.md#ctx-on) * [ctx.once](../service/events.md#ctx-once) * [ctx.parallel](../service/events.md#ctx-parallel) * [ctx.permissions](../service/permissions.md) * [ctx.plugin](../service/registry.md#ctx-plugin) * [ctx.registry](../service/registry.md) * [ctx.scope](../service/registry.md#ctx-scope) * [ctx.serial](../service/events.md#ctx-serial) * [ctx.server](../service/server.md) * [ctx.setInterval](../service/timer.md#ctx-setinterval) * [ctx.setTimeout](../service/timer.md#ctx-settimeout) * [ctx.sleep](../service/timer.md#ctx-sleep) * [ctx.stop](../service/registry.md#ctx-stop) * [ctx.throttle](../service/timer.md#ctx-throttle) * [ctx.timer](../service/timer.md) * [ctx.union](../service/filter.md#ctx-union) 除此以外,以下服务由官方插件提供,因此使用时需要 [声明依赖](../../guide/plugin/service.md#inject): * [ctx.console](../console/server.md) * [ctx.database](../database/database.md) * [ctx.mock](../../plugins/develop/mock.md) * [ctx.server](../../plugins/develop/server.md) ## 实例属性 ### ctx.root.config * 类型: [`Context.Config`](./app.md) 当前的 Koishi 全局配置,相当于配置文件中的配置经过默认值处理后的结果。 ### ctx.baseDir * 类型: `string` 当前的 Koishi 默认路径。如果你使用配置文件,则这个路径是配置文件所在的路径;否则这个路径是当前工作路径。 ### ctx.bots * 类型: `Bot[]` 当前应用的全部机器人实例。 ## 实例方法 ### ctx.extend(meta) * **meta:** `Partial` 要覆盖的属性 * 返回值: `this` 新的上下文 以当前上下文为原型创建一个新上下文。`meta` 中的属性将覆盖当前上下文的属性。 ### ctx.command(def, desc?, config?) * **def:** `string` 指令名以及可能的参数 * **desc:** `string` 指令的描述 * **config:** `CommandConfig` 指令的配置 * **checkUnknown:** `boolean` 是否对未知选项进行检测,默认为 `false` * **checkArgCount:** `boolean` 是否对参数个数进行检测,默认为 `false` * **authority:** `number` 最低调用权限,默认为 `1` * **showWarning:** `boolean` 当小于最短间隔时是否进行提醒,默认为 `true` * 返回值:[`Command`](./command.md) 注册或修改的指令 在当前上下文中注册或修改一个指令。 ### ctx.broadcast(channels?, content) 需要数据库 * **channels:** `string[]` 频道列表,格式为 `{platform}:{channelId}` (如 `discord:1234567890`) * **content:** `string` 要发送的内容 * 返回值: `Promise` 成功发送的消息 ID 列表 所有机器人向自己分配的频道广播消息。如果传入的频道不存在,会输出一个警告。 ### ctx.logger(scope?) * **scope:** `string` 要指定的类型,默认为 `''` * 返回值: [`Logger`](../utils/logger.md) 根据命名空间生成一个 [Logger](../utils/logger.md) 对象。 ## 静态属性和方法 ### Context.filter * 类型: `symbol` ### Context.current * 类型: `symbol` 特殊的键值,可以在通用上下文属性对象的方法上访问。参见 [声明通用上下文属性](../../guide/plugin/service.md#声明通用上下文属性)。 --- --- url: /api/console/context.md --- # 上下文 API ::: tip 参见:[开发 > 控制台 > 客户端开发](../../guide/console/client.md) ::: ::: warning 消歧义:本文档介绍的是控制台客户端的 `Context`,而非 Koishi 本身的 `Context`。要查看后者的 API 文档请 [前往这里](../core/context.md)。 ::: Koishi 的控制台客户端一样通过插件化的方式加载,因此我们也实现了一套类似的 `Context` 机制。许多你熟悉的底层 API 在客户端是依然可用的 (例如生命周期事件、服务的定义与使用等等)。此外,我们还提供了一些与客户端相关的 API 来帮助你更方便地开发控制台插件。 ## 混入属性和方法 以下的属性和方法是由服务混入的。你可以就像使用实例属性和方法一样使用它们。这些 API 的具体用法在对应的服务文档中详细介绍,你可以点击链接前往查看。 * [ctx.bail](../service/events.md#ctx-bail) * [ctx.before](../service/events.md#ctx-before) * [ctx.emit](../service/events.md#ctx-emit) * [ctx.middleware](../service/events.md#ctx-middleware) * [ctx.off](../service/events.md#ctx-off) * [ctx.on](../service/events.md#ctx-on) * [ctx.once](../service/events.md#ctx-once) * [ctx.parallel](../service/events.md#ctx-parallel) * [ctx.plugin](../service/registry.md#ctx-plugin) * [ctx.scope](../service/registry.md#ctx-scope) * [ctx.serial](../service/events.md#ctx-serial) * [ctx.using](../service/registry.md#ctx-using) ## 实例方法 ### ctx.action(id, options) 实验性 * **id:** `string` 动作标识符 * **options.disabled:** `(() => boolean)?` 是否隐藏 * **options.action:** `Function` 回调函数函数 注册一个动作。 ### ctx.menu(id, items) 实验性 * **id:** `string` 菜单标识符 * **items:** `MenuItem[]` 菜单项列表 * **items\[].id:** `string` 动作标识符 * **items\[].label:** `MaybeGetter?` 菜单项名称 * **items\[].type:** `MaybeGetter?` 菜单项类型 * **items\[].icon:** `MaybeGetter?` 菜单项图标 * **items\[].order:** `number?` 排列优先级 注册一个菜单。 ### ctx.page(options) * **options.id:** `string?` 页面标识符 * **options.path:** `string` 页面路由 * **options.name:** `MaybeRefOrGetter` 页面名称 * **options.desc:** `MaybeRefOrGetter?` 页面描述 * **options.icon:** `MaybeRefOrGetter?` 页面图标 * **options.position:** `'top' | 'bottom'` 图标在活动栏中的位置 * **options.order:** `number?` 排列优先级 * **options.disabled:** `(() => boolean)?` 是否隐藏 * **options.component:** `Component` 页面组件 注册一个页面。 ### ctx.settings(options) 实验性 * **options.type:** `string` 所属类别 * **options.title:** `string?` 类别名称 * **options.order:** `number?` 排列优先级 * **options.disabled:** `(() => boolean)?` 是否隐藏 * **options.schema:** `Schema?` 配置构型 * **options.component:** `Component?` 配置组件 扩展用户设置。当使用了 `component` 时,`schema` 会被忽略。 ### ctx.slot(options) * **options.type:** `string` 插槽名称 * **options.order:** `number?` 排列优先级 * **options.disabled:** `(() => boolean)?` 是否隐藏 * **options.component:** `Component` 注入组件 向特定插槽注入组件。 ### ctx.theme(options) * **options.id:** `string` 主题标识符 (必须以 `-dark` 或 `-light` 结尾) * **options.name:** `string` 主题名称 * **options.components:** `Dict?` 主题提供的布局组件 定义一个新的主题。 --- --- url: /guide/basic/middleware.md --- # 中间件 有了接收事件和发送消息的能力,似乎你就能完成一切工作了——很多机器人框架也的确是这么想的。但是从 Koishi 的角度,这还远远不够。因为当我们面临更复杂的需求时,新的问题也会随之产生:如何限制消息能触发的应答次数?如何进行权限管理?如何提高机器人的性能?这些问题的答案将我们引向另一套更高级的系统——这也就是中间件的由来。 中间件是对消息事件处理流程的再封装。你注册的所有中间件将会由一个事件监听器进行统一管理,数据流向下游,控制权流回上游——这可以有效确保了任意消息都只被处理一次。被认定为无需继续处理的消息不会进入下游的中间件——这让我们能够轻易地实现权限管理。与此同时,Koishi 的中间件也支持异步调用,这使得你可以在中间件函数中实现任何逻辑。事实上,相比更加底层地调用事件监听器,**使用中间件处理消息才是 Koishi 更加推荐的做法**。 ::: tip 与事件系统的通用性不同,中间件专注于消息事件。你不能使用中间件处理其他类型的事件。 ::: ## 基本用法 还记得上一节介绍的 [基本示例](./events.md#基本用法) 吗?让我们把它改成中间件的形式: ```ts // 如果收到“天王盖地虎”,就回应“宝塔镇河妖” ctx.middleware((session, next) => { if (session.content === '天王盖地虎') { return '宝塔镇河妖' } else { // 如果去掉这一行,那么不满足上述条件的消息就不会进入下一个中间件了 return next() } }) ``` 中间件与事件的写法非常相似,但有三点区别: * 事件使用 `ctx.on()` 注册,而中间件使用 `ctx.middleware()` 注册 * 中间件的回调函数接受额外的第二个参数 `next`,只有调用了它才会进入接下来的流程 * 中间件支持直接返回要发送的内容,而事件需要手动调用 `session.send()` 同事件类似,注册中间件时也会返回一个新的函数,调用这个函数就可以取消该中间件: ```ts declare const callback: import('koishi').Middleware // ---cut--- const dispose = ctx.middleware(callback) dispose() // 取消中间件 ``` ## 异步中间件 中间件也可以是异步的。下面给出一个示例: ```ts ctx.middleware(async (session, next) => { // 获取数据库中的用户信息 // 这里只是示例,事实上 Koishi 会自动获取数据库中的信息并存放在 session.user 中 const user = await session.getUser(session.userId) if (user.authority === 0) { return '抱歉,你没有权限访问机器人。' } else { return next() } }) ``` ::: warning 注意 异步中间件代码中,`next` 函数被调用时前面必须加上 await (或者 return)。如果删去将可能会导致时序错误,这在 Koishi 中将会抛出一个运行时警告。 ::: ## 前置中间件 从上面的两个例子中不难看出,中间件是一种消息过滤的利器。但是反过来,当你需要的恰恰是捕获全部消息时,中间件反而不会是最佳选择——因为更早注册的中间件可能会将消息过滤掉,导致你注册的回调函数根本不被执行。因此在这种情况下,我们更推荐使用事件监听器。然而,还存在着这样一种情况:你既需要捕获全部的消息,又要对其中的一些加以回复,这又该怎么处理呢? 听起来这种需求有些奇怪,让我们举个具体点例子吧:假如你写的是一个复读插件,它需要在每次连续接收到 3 条相同消息时进行复读。我们不难使用事件监听器实现这种逻辑: ```ts let times = 0 // 复读次数 let message = '' // 当前消息 ctx.on('message', (session) => { // 这里其实有个小问题,因为来自不同群的消息都会触发这个回调函数 // 因此理想的做法应该是分别记录每个群的当前消息和复读次数 // 但这里我们假设机器人只处理一个群,简化示例的逻辑 if (session.content === message) { times += 1 if (times === 3) session.send(message) } else { times = 0 message = session.content } }) ``` 但是这样写出的机器人就存在所有用事件监听器写出的机器人的通病——如果这条消息本身可以触发其他回应,机器人就会多次回应。更糟糕的是,你无法预测哪一次回应先发生,因此这样写出的机器人就会产生延迟复读的迷惑行为。为了避免这种情况发生,Koishi 对这种情况也有对应的解决方案,那就是 **前置中间件**。 与前置事件类似,向 `ctx.middleware()` 传入额外的第二个参数 `true` 以注册前置中间件。所有消息会优先经过前置中间件,像事件监听器一样,并且你获得了决定这条消息是否继续触发其他中间件的能力,这是事件监听器所不具有的。 ```ts let times = 0 // 复读次数 let message = '' // 当前消息 ctx.middleware((session, next) => { if (session.content === message) { times += 1 if (times === 3) return message } else { times = 0 message = session.content return next() } }, true /* true 表示这是前置中间件 */) ``` ## 临时中间件 有的时候,你也可能需要实现这样一种逻辑:你的中间件产生了一个响应,但你认为这个响应优先级较低,希望等后续中间件执行完毕后,如果信号仍然未被截取,就执行之前的响应。这当然可以通过注册新的中间件并取消的方法来实现,但是由于这个新注册的中间件可能不被执行,你需要手动处理许多的边界情况。 为了应对这种问题 Koishi 提供了更加方便的写法:你只需要在调用 `next` 时再次传入一个回调函数即可!这个回调函数只接受一个 `next` 参数,且只会加入当前的中间件执行队列;无论这个回调函数执行与否,在本次中间件解析完成后,它都会被清除。下面是一个例子: ```ts ctx.middleware((session, next) => { if (session.content === 'hlep') { // 如果该 session 没有被截获,则这里的回调函数将会被执行 return next('你想说的是 help 吗?') } else { return next() } }) ``` 除此以外,临时中间件还有下面的用途。让我们先回到上面介绍的前置中间件。它虽然能够成功解决问题,但是如果有两个插件都注册了类似的前置中间件,就仍然可能发生一个前置中间件截获了消息,导致另一个前置中间件获取不到消息的情况发生。但是,借助临时中间件,我们便有了更好的解决方案: ```ts let times = 0 // 复读次数 let message = '' // 当前消息 ctx.middleware((session, next) => { if (session.content === message) { times += 1 if (times === 3) return next(message) } else { times = 0 message = session.content return next() } }, true) ``` 搭配使用上面几种中间件,你的机器人便拥有了无限可能。在 koishi-plugin-repeater 库中,就有着一个官方实现的复读功能,它远比上面的示例所显示的更加强大。如果想深入了解中间件机制,可以去研究一下这个功能的 [源代码](https://github.com/koishijs/koishi-plugin-repeater/blob/main/src/index.ts)。 --- --- url: /manual/starter/android.md --- # 为 Android 安装 ::: tip 如果想了解其他安装方式,请移步 [选择安装方式](./index.md)。 ::: ::: danger 此项目仍在早期开发中。部分功能可能不可用。 ::: ## 下载 * [下载标准包 (.apk)](https://k.ilharp.cc/android-lite.apk) * [下载扩展包 (.apk)](https://k.ilharp.cc/android-full.apk) * [查看更多版本](https://github.com/koishijs/koishi-android/releases) ## 安装 通过安装包安装 Koishi 是最推荐的安装方式,安装程序会自动处理大部分问题。一般情况下你都应该选择这种方式。 ## 启动 安装完成后,点击 Koishi 图标即可启动。 --- --- url: /manual/starter/linux.md --- # 为 Linux 安装 ::: tip 如果想了解其他安装方式,请移步 [选择安装方式](./index.md)。 ::: ## 下载 * [AppImage (.AppImage)](https://k.ilharp.cc/linux.AppImage) * [查看更多版本](https://github.com/koishijs/koishi-desktop/releases) ## 启动 启动你刚刚下载的程序,你应当可以在 `http://localhost:5140` 访问 Koishi 的网页控制台。 ## 其他选择 找不到适合你的安装包?你仍然可以选择 [在容器中使用](./docker.md) 或 [创建模板项目](./boilerplate.md)。 --- --- url: /manual/starter/macos.md --- # 为 macOS 安装 ::: tip 如果想了解其他安装方式,请移步 [选择安装方式](./index.md)。 ::: ## 下载 * [安装包 (.pkg)](https://k.ilharp.cc/osx.pkg) * [查看更多版本](https://github.com/koishijs/koishi-desktop/releases) ## 安装 通过安装包安装 Koishi 是最推荐的安装方式,安装程序会自动处理大部分问题。一般情况下你都应该选择这种方式。 ## 启动 安装完成后,点击 Koishi 图标即可启动。等待几秒钟,Koishi 控制台将会被打开,现在你可以开始使用 Koishi 了。 ## 其他选择 找不到适合你的安装包?你仍然可以选择 [在容器中使用](./docker.md) 或 [创建模板项目](./boilerplate.md)。 --- --- url: /manual/starter/windows.md --- # 为 Windows 安装 ::: tip 如果想了解其他安装方式,请移步 [选择安装方式](./index.md)。 ::: ## 下载 * [安装包 (.msi)](https://k.ilharp.cc/win.msi) * [查看更多版本](https://github.com/koishijs/koishi-desktop/releases) ## 安装 通过安装包安装 Koishi 是最推荐的安装方式,安装程序会自动处理大部分问题。一般情况下你都应该选择这种方式。 ![msi-installer](/manual/windows/msi-installer-dark.webp) {.dark-only} ![msi-installer](/manual/windows/msi-installer-light.webp) {.light-only} 双击安装包并一路点按「Next」,等待安装过程结束,你便可以在「开始」菜单中找到 Koishi 了。 ::: tip 安装流程在部分电脑上这可能会花费 10 分钟以上的时间,请耐心等待。 ::: ## 启动 ![start-menu](/manual/windows/start-menu-dark.webp) {.dark-only} ![start-menu](/manual/windows/start-menu-light.webp) {.light-only} 安装完成后,点击 Koishi 图标即可启动。等待几秒钟,Koishi 控制台将会被打开,现在你可以开始使用 Koishi 了。 ## 其他选择 找不到适合你的安装包?你仍然可以选择 [在容器中使用](./docker.md) 或 [创建模板项目](./boilerplate.md)。 --- --- url: /guide/console/theme.md --- # 主题开发 实验性 Koishi 也允许插件定义控制台主题。让我们先从简单的色彩主题开始。 ## 色彩主题 [theme-vanilla](https://github.com/koishijs/koishi-plugin-theme-vanilla) 插件提供了许多色彩主题,你可以参考它的源码来开发自己的主题: ```ts // 引入定义主题色的 CSS 文件 import './index.scss' ctx.theme({ id: 'coffee-dark', name: 'Coffee Dark', }) ctx.theme({ id: 'coffee-light', name: 'Coffee Light', }) ``` 如你所见,要定义一个色彩主题非常简单,只需要调用 [`ctx.theme()`](../../api/console/context.md#ctx-theme) 即可。其中,`id` 用于标识主题,`name` 则会显示在主题选择器中。 ::: tip 请注意:`id` 必须以 `-dark` 或 `-light` 结尾,否则 Koishi 将无法正确回退 CSS 变量! ::: 接下来让我们看看 `index.scss` 的内容: ```scss :root, .theme-root { &[theme=coffee-dark] { --bg1: #231F1E; --bg2: #262120; --bg3: #292423; --bg4: #342A27; } &[theme=coffee-light] { --bg1: #EEE9E7; --bg2: #EAE4E1; --bg3: #E6DFDC; --bg4: #E3DBD6; } } ``` Koishi 为主题系统预定义了许多 CSS 变量。只需要在属性选择器中修改这些变量的值,一个色彩主题就大功告成了。 ## 改变布局 Koishi 同样允许主题改变控制台的布局: ```ts import root from './root.vue' import layout from './layout.vue' ctx.theme({ id: 'windows-light', name: 'Windows Light', components: { root, layout }, }) ``` 在上面的例子中,[`ctx.theme()`](../../api/console/context.md#ctx-theme) 接受一个额外的 `components` 选项,用于覆盖默认的布局组件。其中,`root` 用于定制整个控制台的布局,`layout` 用于定制 `` 的实现。 --- --- url: /api/core/events.md --- # 事件 (Events) ::: tip 参见:[开发 > 交互基础 > 事件系统](../../guide/basic/events.md) 参见:[开发 > 模块化 > 生命周期](../../guide/plugin/lifecycle.md) ::: ::: tip 本节介绍 Koishi 的内置事件。如果想了解事件 API,请前往 [API > 内置服务 > 事件](../service/events.md)。 ::: ## 通用会话事件 通用会话事件由适配器实现,它们均包含一个 [`session`](./session.md) 参数,触发方式均为 `emit`。各事件会在相应的资源页面中介绍。 * [friend-request](../resources/user.md#friend-request) * [guild-added](../resources/guild.md#guild-added) * [guild-member-added](../resources/member.md#guild-member-added) * [guild-member-removed](../resources/member.md#guild-member-removed) * [guild-member-request](../resources/member.md#guild-member-request) * [guild-member-updated](../resources/member.md#guild-member-updated) * [guild-removed](../resources/guild.md#guild-removed) * [guild-request](../resources/guild.md#guild-request) * [guild-role-created](../resources/role.md#guild-role-created) * [guild-role-deleted](../resources/role.md#guild-role-deleted) * [guild-role-updated](../resources/role.md#guild-role-updated) * [guild-updated](../resources/guild.md#guild-updated) * [login-added](../resources/login.md#login-added) * [login-removed](../resources/login.md#login-removed) * [login-updated](../resources/login.md#login-updated) * [message-created (message)](../resources/message.md#message-created) * [message-deleted](../resources/message.md#message-deleted) * [message-updated](../resources/message.md#message-updated) * [reaction-added](../resources/reaction.md#reaction-added) * [reaction-removed](../resources/reaction.md#reaction-removed) ## 内置会话事件 与上面介绍的通用会话事件不同,这里的事件都是 Koishi 自身实现的,它们有不同的触发方式,但是都会支持上下文选择器。 ### 事件:middleware * **session:** `Session` 当前会话 * **触发方式:** emit 在执行完全部中间件后会在对应的上下文触发。 ### 事件:before-parse * **content:** `string` 要解析的文本 * **session:** `Session` 当前会话 * **触发方式:** bail 尝试将文本解析成 Argv 对象时调用。你可以在回调函数中返回一个 Argv 对象以覆盖默认的解析行为。 ### 事件:parse * **argv:** `Argv` 运行时参数 * **触发方式:** bail 尝试将一个未识别出指令的 Argv 对象识别成指令调用时使用。你可以在回调函数中修改传入的 Argv 对象,或者返回一个字符串表示识别出的指令。 ### 事件:before-attach-channel ### 事件:before-attach-user * **session:** `Session` 当前会话 * **fields:** `Set` 要获取的字段列表 * **触发方式:** emit 当 Koishi 试图从数据库获取频道 / 用户信息前触发。你可以在回调函数中通过 `fields.add()` 修改传入的字段集合,增加的字段将可以被指令以及之后的中间件获取到。 这两个事件的触发于内置中间件中。如果没有配置数据库,则两个事件都不会触发;如果不是群聊消息,则 before-attach-channel 事件不会触发。 ### 事件:attach-channel ### 事件:attach-user * **session:** `Session` 当前会话 * **触发方式:** serial 当 Koishi 完成频道 / 用户数据获取后触发。调用时会传入一个 Session 对象,将会拥有 `channel`/`user` 属性。你可以在回调函数中修改这两个属性,这些修改会在后续过程中自动更新到数据库。如果你在回调函数中返回一个 truthy 值,则该会话不会触发指令以及之后的中间件。 如果没有配置数据库,则两个事件都不会触发;如果不是群聊消息,则 attach-channel 事件不会触发。 ### 事件:command/before-attach-channel ### 事件:command/before-attach-user * **session:** `Argv` 运行时参数 * **fields:** `Set` 要获取的字段列表 * **触发方式:** emit 当 Koishi 试图从数据库获取频道 / 用户信息前触发。你可以在回调函数中通过 `fields.add()` 修改传入的字段集合,增加的字段将可以被指令以及之后的中间件获取到。 这两个事件触发于任意指令调用前。如果没有配置数据库,则两个事件都不会触发;如果不是群聊消息,则 before-attach-channel 事件不会触发。 ### 事件:before-send * **session:** `Session` 消息会话 * **触发方式:** bail 即将发送信息时会在对应的上下文触发。调用时会传入一个事件类型为 [send](#消息类事件) 的会话实例。由于该消息还未发送,这个会话并没有 `messageId` 属性。你可以通过修改 `session.content` 改变发送的内容,或者返回一个 truthy 值以取消该消息的发送。 ### 事件:command/before-execute * **argv:** `Argv` 运行时参数 * **触发方式:** serial 调用指令前会在对应的上下文触发。此时指令的可用性还未经检测,因此可能出现参数错误、权限不足、超过使用次数等情况。你可以通过在回调函数中返回一个字符串以取消该指令的执行。进一步,如果该字符串非空,则会作为此指令执行的结果。 ### 事件:command * **argv:** `Argv` 运行时参数 * **触发方式:** parallel 指令调用完毕后会在对应的上下文触发。 ## 生命周期事件 这里的所有事件在全体上下文触发的 (即上下文选择器对这些事件无效)。 ### 事件:ready * **触发方式:** parallel 应用启动时触发。如果应用已经处于启动状态,则会立即触发。参见 [异步加载](../../guide/plugin/lifecycle.md#异步加载与-ready-事件)。 ### 事件:dispose * **触发方式:** parallel 插件被卸载时触发。参见 [清除副作用](../../guide/plugin/lifecycle.md#清除副作用)。 ::: warning 请注意,`dispose` 事件的目的是清理副作用而不是确保数据保存。当 Koishi 进程崩溃或是被强行中止时,`dispose` 事件都可能不会触发。为了保护你的数据,你应当在每一次修改后立即上传数据,而不是在 `dispose` 中处理收尾工作。 ::: ### 事件:service * **name:** `string` 服务名称 * **触发方式:** emit 有服务被修改时触发。 ### 事件:model * **name:** `string` 被扩展的表名 * **触发方式:** emit 调用 `model.extend()` 时触发。 ### 事件:login-added * **bot:** [`Bot`](./bot.md) 机器人实例 * **触发方式:** emit 添加机器人时触发。 ### 事件:login-removed * **bot:** [`Bot`](./bot.md) 机器人实例 * **触发方式:** emit 移除机器人时触发。 ### 事件:login-updated * **bot:** [`Bot`](./bot.md) 机器人实例 * **触发方式:** emit 机器人状态发生改变时触发。 --- --- url: /guide/basic/events.md --- # 事件系统 在上一节中我们了解了指令开发,现在让我们回到更加基础的事件系统。事件系统在 Koishi 中扮演着底层的角色,它不仅包含由聊天平台触发的会话事件,还包含了监听运行状态的生命周期事件和提供扩展性的自定义事件。 ## 基本用法 让我们先从一个基本示例开始: ```ts ctx.on('message', (session) => { if (session.content === '天王盖地虎') { session.send('宝塔镇河妖') } }) ``` 上述代码片段实现了一个简单的功能:当任何用户发送「天王盖地虎」时,机器人将发送「宝塔镇河妖」。如你所见,`ctx.on()` 方法监听了一个事件。传入的第一个参数 `message` 是事件的名称,而第二个参数则是事件的回调函数。每一次 `message` 事件被触发 (即收到消息) 时都会调用该函数。 回调函数接受一个参数 `session`,称为**会话对象**。在这个例子中,我们通过它访问事件相关的数据 (使用 `session.content` 获取消息的内容),并调用其上的 API 作为对此事件的响应 (使用 `session.send()` 在当前频道内发送消息)。 事件与会话构成了最基础的交互模型。这种模型不仅能够处理消息,还能够处理其他类型的事件。我们再给出一个例子: ```ts // 当有好友请求时,接受请求并发送欢迎消息 ctx.on('friend-request', async (session) => { // session.bot 是当前会话绑定的机器人实例 await session.bot.handleFriendRequest(session.messageId, true) await session.bot.sendPrivateMessage(session.userId, '很高兴认识你!') }) ``` 像这样由聊天平台推送的事件,我们称之为 **会话事件**。除此以外,Koishi 还有着其他类型的事件,例如由 Koishi 自身生成的 **生命周期事件**,又或者是由插件提供的 **自定义事件** 等等。这些事件的监听方式与会话事件基本一致,只不过它们的回调函数接受的参数不同。例如下面的代码实现了当 Bot 上线时自动给自己发送一条消息的功能: ```ts // bot-status-updated 不是会话事件 // 所以回调函数接受的参数不是 session 而是 bot ctx.on('bot-status-updated', (bot) => { if (bot.status === Status.ONLINE) { // 这里的 userId 换成你的账号 bot.sendPrivateMessage(userId, '我上线了~') } }) ``` 在后续的章节中,我们也将介绍更多的事件和会话的使用方法。 ## 监听事件 在上面的例子中,我们已经了解到事件系统的基本用法:使用 `ctx.on()` 注册监听器。它的写法与 Node.js 自带的 [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter) 类似:第一个参数表示要监听的事件名称,第二个参数表示事件的回调函数。同时,我们也提供了类似的函数 `ctx.once()`,用于注册一个只触发一次的监听器;以及 `ctx.off()`,用于取消一个已注册的监听器。 这套事件系统与 EventEmitter 的一个不同点在于,无论是 `ctx.on()` 还是 `ctx.once()` 都会返回一个 dispose 函数,调用这个函数即可取消注册监听器。因此你其实不必使用 `ctx.once()` 和 `ctx.off()`。下面给一个只触发一次的监听器的例子: ```ts declare module 'koishi' { interface Events { foo(...args: any[]): void } } // ---cut--- // 回调函数只会被触发一次 const dispose = ctx.on('foo', (...args) => { dispose() // do something }) ``` ### 事件的命名 无论是会话事件,生命周期事件还是插件自定义的事件,Koishi 的事件名都遵循着一些既定的规范。遵守规范能够让开发者获得一致的体验,提高开发和调试的效率。它们包括: * 总是使用 param-case 作为事件名 * 通过命名空间进行管理,使用 `/` 作为分隔符 * 配对使用 xxx 和 before-xxx 命名具有时序关系的事件 举个例子,koishi-plugin-dialogue 扩展了多达 20 个自定义事件。为了防止命名冲突,所有的事件都以 `dialogue/` 开头,并且在特定操作前触发的事件都包含了 `before-` 前缀,例如: * dialogue/before-search: 获取搜索结果前触发 * dialogue/search: 获取完搜索结果后触发 ### 前置事件 前面介绍了,Koishi 有不少监听器满足 before-xxx 的形式。对于这类监听器的注册,我们也提供了一个语法糖,那就是 `ctx.before('xxx', callback)`。这种写法也支持命名空间的情况: ```ts // @errors: 2304 ctx.before('dialogue/search', callback) // 相当于 ctx.on('dialogue/before-search', callback, true) ``` 默认情况下,事件的多个回调函数的执行顺序取决于它们添加的顺序。先注册的回调函数会先被执行。如果你希望提高某个回调函数的优先级,可以给 `ctx.on()` 传入第三个参数 `prepend`,设置为 true 即表示添加到事件执行队列的开头而非结尾,相当于 [`emitter.prependListener()`](https://nodejs.org/api/events.html#emitterprependlistenereventname-listener)。 对于 `ctx.before()`,情况则正好相反。默认的行为的先注册的回调函数后执行,同时 `ctx.before()` 的第三个参数 `append` 则表示添加到事件执行队列的末尾而非开头。 ## 触发事件 如果你开发的插件希望允许其他插件扩展,那么触发事件就是最简单的方式。 触发事件的基本用法也都与 EventEmitter 类似,第一个参数是事件名称,之后的参数对应回调函数的参数。下面是一个例子: ```ts declare module 'koishi' { interface Events { 'custom-event'(...args: any[]): void } } // ---cut--- // @errors: 2304 ctx.emit('custom-event', arg1, arg2, ...rest) // 对应于 ctx.on('custom-event', (arg1, arg2, ...rest) => {}) ``` ### 触发方式 Koishi 的事件系统与 EventEmitter 的另一个区别在于,触发一个事件可以有着多种形式,目前支持 4 个不同的方法,足以适应绝大多数需求。 * emit: 同时触发所有 event 事件的回调函数 * parallel: 上述方法对应的异步版本 * bail: 依次触发所有 event 事件的回调函数;当返回一个 `false`, `null`, `undefined` 以外的值时将这个值作为结果返回 * serial: 上述方法对应的异步版本 此外,你还将在下一节学习 [中间件](./middleware.md),它提供了一种更加强大的消息事件处理流程。 ### 过滤触发上下文 如果你的自定义事件与某个特定会话相关 (并不需要是会话事件),你可以在触发事件的时候传入一个额外的一参数 `session`,以实现对触发上下文的过滤: ```ts declare module 'koishi' { interface Events { 'custom-event'(...args: any[]): void } } // ---cut--- // @errors: 2304 // 无法匹配该会话的上下文中注册的回调函数不会被执行 (可能有点绕) ctx.emit(session, 'custom-event', arg1, arg2, ...rest) ``` 过滤触发上下文的效果将在 [过滤器](../plugin/filter.md) 一节中详细介绍。 更一般地,即使是不使用会话的事件也能主动选择触发的上下文,其语法完全一致: ```ts const thisArg = { [Context.filter]: callback } ctx.emit(thisArg, 'custom-event', arg1, arg2, ...rest) ``` 触发事件时传入的一参数如果是对象,则会作为事件回调函数的 `this`。并且如果这个对象有 `Context.filter` 属性,那么这个属性将被用于过滤触发上下文。对应的值是一个函数,传入一个上下文对象,返回一个 boolean 表示是否应该在该上下文上触发该事件。而上面介绍的会话事件只是一种特殊情况而已。 ## 自定义事件 在本节的最后,我们来聊聊插件扩展的事件系统。 如果你是插件的开发者,想要自定义一些事件,那么只需要在你的插件中添加下面的代码: ```ts{5} declare module 'koishi' { interface Events { // 方法名称对应自定义事件的名称 // 方法签名对应事件的回调函数签名 'kook/message-btn-click'(...args: any[]): void } } ``` 如果你监听的事件由其他插件扩展而来,那么你同样需要通过一行额外的代码来导入相应的类型: ```ts{4} // 从 @koishijs/plugin-adapter-kook 导入事件类型 // 这里的 import {} from 会在编译时被删除,不会影响运行时的行为 // 请不要写成 import '@koishijs/plugin-adapter-kook' import {} from '@koishijs/plugin-adapter-kook' // 如果没有上面的类型导入,下面的代码会报错 ctx.on('kook/message-btn-click', callback) ``` --- --- url: /api/service/events.md --- # 事件系统 (Events) ::: tip 参见:[开发 > 交互基础 > 事件系统](../../guide/basic/events.md) 参见:[开发 > 模块化 > 生命周期](../../guide/plugin/lifecycle.md) ::: ::: tip 本节介绍事件 API。如果想了解 Koishi 的内置事件,请前往 [API > 核心模块 > 事件](../core/events.md)。 ::: ## 实例方法 ### ctx.emit(session?, event, ...param) ### ctx.parallel(session?, event, ...param) * **session:** [`Session`](../core/session.md) 会话对象 * **event:** `string` 事件名称 * **param:** `any[]` 事件的参数 * 返回值: `boolean` 匹配结果 同时触发所有 event 事件的能够匹配 session 对象的回调函数。emit 为同步,parallel 为异步。 ### ctx.bail(session?, event, ...param) ### ctx.serial(session?, event, ...param) * **session:** [`Session`](../core/session.md) 会话对象 * **event:** `string` 事件名称 * **param:** `any[]` 事件的参数 * 返回值: `boolean` 匹配结果 依次触发所有 event 事件的能够匹配 session 对象的回调函数。当返回一个 false, null, undefined 以外的值时将这个值作为结果返回。bail 为同步,serial 为异步。 ### ctx.on(event, listener, prepend?) * **event:** `string` 事件名称 * **listener:** `Function` 回调函数 * **prepend:** `boolean` 是否前置 * 返回值: `() => boolean` 取消这个监听器 监听一个事件。 ### ctx.off(event, listener) * **event:** `string` 事件名称 * **listener:** `Function` 回调函数 * 返回值: `boolean` 是否有此回调函数 取消监听一个事件。 ### ctx.once(event, listener, prepend?) * **event:** `string` 事件名称 * **listener:** `Function` 回调函数 * **prepend:** `boolean` 是否前置 * 返回值: `() => boolean` 取消这个监听器 监听一个事件,且确保回调函数只会被执行一次。 ### ctx.before(event, listener, append?) * **event:** `string` 事件名称 * **listener:** `Function` 回调函数 * **append:** `boolean` 是否后置 * 返回值: `() => boolean` 取消这个监听器 监听一个以 `before-` 开头的事件。 ### ctx.middleware(middleware, prepend?) * **middleware:** [`Middleware`](../../guide/basic/middleware.md#使用中间件) 要注册的中间件 * **prepend:** `boolean` 是否前置 * 返回值: `() => boolean` 取消这个中间件 当前上下文中注册一个中间件。 --- --- url: /api/resources/interaction.md --- # 交互 (Interaction) 实验性 ## 类型定义 ```ts interface Argv { name: string arguments: string[] options: Dict } interface Button { id: string } ``` ## 事件 ### interaction/button * **session:** `Session` 会话对象 * 触发方式: emit 类型为 `action` 的按钮被点击时触发。 ### interaction/command * **session:** `Session` 会话对象 * 触发方式: emit 调用斜线指令时触发。 --- --- url: /manual/introduction.md --- # 介绍 Koishi 是一个跨平台、可扩展、高性能的聊天机器人框架。 它的名字和图标设计来源于东方 Project 中的角色 [古明地恋 (Komeiji Koishi)](https://zh.touhouwiki.net/wiki/古明地恋)。古明地恋是一个会做出无意识举动的角色,取这个名字既象征着聊天机器人的主题,也蕴含了开发者为之倾注的热爱。 ## 特性 ### 开箱即用 Koishi 提供了开箱即用的控制台。即便是零基础的用户也能在几分钟之内搭建自己的聊天机器人。 * 提供在线插件市场,即使没有任何编程基础,也能轻松在控制台中下载安装插件 * 支持 QQ,Telegram,Discord 等主流聊天平台,支持多账户和跨平台数据互通 * 随时随地通过控制面板监控运行状态,控制机器人的行为,甚至上号聊天 ### 生态丰富 经过了长达四年的迭代,Koishi 已经发展出了丰富的插件生态和与之匹配的健壮系统。超过 3000 个官方和社区插件覆盖了机器人开发的方方面面,从平台支持、数据库、资源存储、网页控制台、状态管理到具体的业务功能一应俱全。无论你的目标是构建大型交互应用还是轻量级的辅助机器人,Koishi 都为你提供了最佳实践。如果担心在复杂的功能中迷失方向,我们也准备了细致的文档来提供帮助。 ### 专为开发者打造 Koishi 更为开发者准备了众多专业功能,使插件开发者得以在各种复杂需求中构建规模化的解决方案。 * 类型支持:Koishi 完全基于 TypeScript 开发,拥有顶级的类型支持,丰富的代码提示让你在编写代码的时候甚至无需查看文档 * 单元测试:所有核心功能均已经通过单元测试,既确保了可靠性,也为开发者提供了一套测试插件和定位问题的最佳实践 * 模块热重载:开发 Koishi 插件时,只需轻点保存即可热重载,无需频繁重启机器人,如同前端开发一样丝滑顺畅 ## 马上起步 点击前往 [起步](./starter/),开启你的 Koishi 之旅。 --- --- url: /about/upgrade.md --- # 从低版本迁移 ## JSX 支持 v4.10.3 在 4.10.3 版本中,我们正式引入了 JSX 支持。这意味着你可以在插件中使用 JSX 语法来构造消息元素了。要实现这一点,你需要对你的项目进行一些配置: 1. 打开 `tsconfig.base.json` 文件并加入以下配置: ```json{3-4} { "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "@satorijs/element", }, } ``` 2. 将要使用 JSX 的文件后缀名修改为 `.tsx`。 ## 心跳更新 v4.10.4 在 4.10.4 版本中,我们调整了默认的心跳行为,这可能导致老用户升级时遭遇无限重启问题。可以执行下列操作进行升级: 1. 先将 market 插件更新到最新版本 (最新版本支持批量更新) 2. (非 v4.10.3 用户忽略此步骤) 在控制台中修改全局设置,将 `heartbeatInterval` 项的值改为 `6000`,`heartbeatTimeout` 项的值改为 `600000`,并点击「重载配置」按钮 3. 在依赖管理中,通过下拉菜单将全部官方依赖修改为最新版本 (如果有 suggest 和 assets-\* 依赖则选择移除),并点击「应用更改」按钮 4. 更新完成后重启实例 5. 在控制台中修改全局设置,将 `heartbeatInterval` 和 `heartbeatTimeout` 项的值均改为 `0`,并点击「重载配置」按钮 6. 再次重启实例 7. 如果之前移除了 assets-\* 插件,请重新前往插件市场进行安装 ## CLI 更新 v4.11.0 在 4.11.0 版本中我们移除了 @koishijs/cli 包,将其合并到了 koishi 中。这意味着你每次升级时不再需要同时升级两边了。但对于已经安装了 @koishijs/cli 的用户,你需要执行下列操作完成升级: 1. 先完成 4.10.4 版本的更新,确保自己的版本号不小于 4.10.4 2. 在依赖管理中,修改 koishi 的版本号到 4.11.0,并移除 @koishijs/cli 3. 点击「应用更改」按钮 ## HMR 更新 v4.12.0 在 4.12.0 版本中,我们将模块热替换相关功能移至专门的插件 @koishijs/plugin-hmr 中。如果你使用了 hmr 插件,你需要执行下列操作完成升级: 1. 安装最新版本的 @koishijs/plugin-hmr 2. 修改你的配置文件,加上 [模块热替换](../guide/develop/script.md#hmr) 中提到的部分 3. 移除 `package.json` 文件中 `scripts.dev` 的 `--watch` 参数 ## 插件市场更新 v4.13.0 在 4.13.0 版本中,我们将 @koishijs/plugin-market 插件分拆为了两个插件 market 和 config。其中 market 负责「插件市场」和「依赖管理」页面,而 config 则负责「插件配置」页面。直接将 market 插件更新到 2.0.0 或以上版本的用户会发现自己的「插件配置」页面消失,此时你需要执行下列操作完成升级: 1. 首先确保你的 market 插件是最新版 (应该是 2.0.0 以上版本) 2. 打开「插件市场」页面,安装最新版的 config 插件 3. 打开「资源管理器」页面,找到 `koishi.yml` 文件,打开并编辑: ```yaml host: 127.0.0.1 port: 5140 maxPort: 5149 plugins: ... ... config: {} # 加一行在这里,注意左侧缩进与 market 对齐 market: ... ... ``` 4. 点击右上角的保存按钮 5. 重新启动 Koishi 实例 ## 国际化更新 v4.13.0 在 4.13.0 版本中,我们也引入了多语言的回退机制。这意味者,所有涉及语言配置的地方都需要从单一的语言字符串修改为数组。具体包括以下几个地方: * 应用配置项 `locale` → `i18n.locales` * 用户和频道的数据结构 `locale` → `locales` * 会话对象的属性 `locale` → `locales` 用户无需留意这些改动,但开发者如果使用了上述 API 则需要进行迁移。 ## 协议更新 v4.14.5 在 4.14.5 版本中,我们将协议库 Satori 升级到了 v3 alpha 版本。新版本引入了与分页 API 相关的不兼容更新。具体受影响的 API 如下: * `bot.getChannelList()` * `bot.getFriendList()` * `bot.getGuildList()` * `bot.getGuildMemberList()` * `bot.getGuildRoleList()` * `bot.getMessageList()` * `bot.getReactionList()` 上述 API 将不再返回 `Promise` 而是返回一个 `Promise>`,包含 `data` 属性和可选的 `next` 属性。`data` 包含了当前页的数据,`next` 则表示下一页的分页令牌。此外,对于上述每一个 API,我们还额外提供了返回异步迭代器的版本: ```ts for (const item of await bot.getChannelList()) // old for await (const item of bot.getChannelIter()) // new ``` ## 协议更新 v4.15.0 在 4.15.0 版本中,我们将协议库 Satori 升级到了 v3 正式版本。新版本引入了一系列涉及平台资源不兼容更新。 [`User`](../api/resources/user.md) 类型的 `userId` 属性改为 `id`,同理对于 [`Channel`](../api/resources/channel.md), [`Guild`](../api/resources/guild.md), [`Message`](../api/resources/message.md) 也是如此。此外,`Author` 被重构为了 `User` 和 `Member` 两个部分。 [`Session`](../api/core/session.md) 引入了 `event` 属性用于存放所有事件相关的资源。尽管我们在会话中提供了 [访问器属性](../api/core/session.md#accessor-property) 以保证了大部分 API 的向下兼容,但对于没有提供访问器的事件属性,或是在使用 Bot API 的返回值时,你都需要手动修改代码。 `Adapter.Server` 和 `Adapter.Client` 两个基类被移除。现在可以直接使用 [`Adapter`](../api/core/adapter.md) 基类,并通过 `reusable` 属性决定其是否可重用。 [`Bot`](../api/core/bot.md) 将实现 [`Login`](../api/resources/login.md) 资源,因此其上的用户数据将存放在 `bot.user` 中。此外,[`status`](../api/resources/login.md) 属性由字符串变为数值枚举。 [`encoder.results`](../api/message/encoder.md#encoder-results) 的类型由 `string[]` 变为 `Message[]`。 新增用于创建私聊频道的 [`bot.createDirectChannel()`](../api/resources/channel.md),因此不再需要实现 [`bot.sendPrivateMessage()`](../api/resources/message.md#bot-sendprivatemessage)。 ## Server 插件独立 v4.16.0 在 4.16.0 版本中,我们将 `Server` 服务从 Koishi 中分离出来,独立成了 [@koishijs/plugin-server](../plugins/develop/server.md) 插件。Koishi CLI 提供了自动迁移机制,因此任何使用 CLI 启动 Koishi 的用户无需进行任何操作。 如果你是将 Koishi 作为依赖调用的进阶开发者,你需要执行下列操作完成升级: 1. 安装最新版本的 [@koishijs/plugin-server](../plugins/develop/server.md) 插件; 2. 在你的代码中手动导入并加载该插件; 3. 将你过去的 `host`, `port`, `maxPort`, `selfUrl` 全局设置移动至 server 插件的配置项。 ## 消息元素更新 v4.16.4 在 4.16.4 版本中,我们将 Koishi 内部的消息元素实现与 Satori 协议规范进行了对齐。涉及到以下 API 的变动: * `` 元素更名为 `` * ``, `