├── temporary
├── data.json
└── index.ts
├── eventsHandle
├── otherPlay
│ ├── runtime
│ │ ├── child.ts
│ │ ├── index.ts
│ │ └── runtime.ts
│ ├── search
│ │ ├── index.ts
│ │ ├── interface.ts
│ │ └── search.ts
│ ├── interface.ts
│ ├── index.ts
│ ├── landlords.ts
│ ├── goodMorning.ts
│ ├── goodNight.ts
│ ├── getCopyWriting.ts
│ ├── getSuggestions.ts
│ ├── xiaoqiuChat.ts
│ ├── makeSuggestion.ts
│ └── listenMusic.ts
├── groupchatAdmin
│ ├── setCard
│ │ ├── index.ts
│ │ ├── interface.ts
│ │ ├── setCard.ts
│ │ └── tools.ts
│ ├── checkDeadPerson
│ │ ├── index.ts
│ │ ├── interface.ts
│ │ ├── tools.ts
│ │ ├── relieveCheckDeadPerson.ts
│ │ └── checkDeadPerson.ts
│ ├── notAllowedSpeak
│ │ ├── index.ts
│ │ ├── tools.ts
│ │ ├── relieveNotAllowedSpeak.ts
│ │ └── notAllowedSpeak.ts
│ ├── index.ts
│ ├── setGroupKick.ts
│ ├── alert.ts
│ ├── delMemberMessage.ts
│ ├── banCommon.ts
│ └── setGroupWholeBan.ts
├── scorePlayMethods
│ ├── draw
│ │ ├── index.ts
│ │ ├── interface.ts
│ │ ├── draw.ts
│ │ ├── tools.ts
│ │ └── drawCount.ts
│ ├── packsack
│ │ ├── index.ts
│ │ ├── tools.ts
│ │ ├── addScore.ts
│ │ └── getPacksack.ts
│ ├── propCard
│ │ ├── index.ts
│ │ ├── banAndDelCard.ts
│ │ └── seeCard.ts
│ ├── index.ts
│ ├── getScoreWay.ts
│ ├── getDrawDiscount.ts
│ └── changeDrawDiscount.ts
├── groupchat
│ ├── index.ts
│ ├── askQuestions.ts
│ ├── signIn.ts
│ └── repeater.ts
├── proxy
│ ├── index.ts
│ ├── interface.ts
│ ├── verifyAllowedSpeak.ts
│ └── verifyAnswer.ts
├── xiaoqiu
│ ├── index.ts
│ ├── tools.ts
│ ├── versions.ts
│ ├── menu.ts
│ ├── isXiaoQiuOnline.ts
│ └── switchCommand.ts
├── arguments
│ ├── menuHelp.ts
│ └── info.ts
├── fns.ts
└── index.ts
├── .gitignore
├── assets
├── images
│ ├── emoji
│ │ ├── 比心2.jpg
│ │ ├── 万用表情
│ │ │ └── default.jpg
│ │ ├── 代码相关
│ │ │ └── default.jpg
│ │ └── 群聊相关
│ │ │ └── default.jpg
│ ├── seeCard
│ │ └── default.jpg
│ ├── groupchat
│ │ ├── 第一届线上前端交流会.jpg
│ │ ├── dark-menu-jfwf.png
│ │ ├── dark-menu-qglcz.png
│ │ ├── dark-menu-qlxg.png
│ │ ├── dark-menu-qtwf.png
│ │ ├── dark-menu-xqxg.png
│ │ ├── dark-version-1.png
│ │ ├── lights-menu-jfwf.png
│ │ ├── lights-menu-qglcz.png
│ │ ├── lights-menu-qlxg.png
│ │ ├── lights-menu-qtwf.png
│ │ ├── lights-menu-xqxg.png
│ │ └── lights-version-1.png
│ └── xiaoqiu
│ │ ├── 浅色模式
│ │ ├── 其它玩法
│ │ │ └── default.jpg
│ │ ├── 小秋相关
│ │ │ └── default.jpg
│ │ ├── 积分玩法
│ │ │ └── default.jpg
│ │ ├── 群聊相关
│ │ │ └── default.jpg
│ │ └── 群管理操作
│ │ │ └── default.jpg
│ │ └── 深色模式
│ │ ├── 其它玩法
│ │ └── default.jpg
│ │ ├── 小秋相关
│ │ └── default.jpg
│ │ ├── 积分玩法
│ │ └── default.jpg
│ │ ├── 群聊相关
│ │ └── default.jpg
│ │ └── 群管理操作
│ │ └── default.jpg
├── readme
│ ├── darks
│ │ └── default.jpg
│ └── lights
│ │ └── default.jpg
├── videos
│ ├── listen
│ │ └── default.jpg
│ └── other
│ │ └── default.jpg
├── package.json
└── xiaoqiu_alpha.sql
├── lib
├── groupchat
│ ├── interface.ts
│ └── index.ts
├── time
│ ├── interface.ts
│ ├── index.ts
│ └── timeUnitTransform.ts
├── methods
│ ├── types.ts
│ ├── createImg.ts
│ ├── index.ts
│ ├── pubsub.ts
│ └── tools.ts
├── interface
│ ├── methods.ts
│ ├── account.ts
│ ├── toolsType.ts
│ ├── index.ts
│ └── command.ts
├── user-copy
│ ├── interface.ts
│ ├── index.ts
│ ├── account.ts
│ └── account-alpha.ts
├── oicq
│ ├── index.ts
│ ├── menuKeywords.ts
│ ├── tools.ts
│ ├── oicqOperations.ts
│ ├── interface.ts
│ └── registerEvents.ts
└── Temp
│ ├── temp.ts
│ └── temp-comment.ts
├── .idea
├── misc.xml
├── modules.xml
├── bot-master.iml
└── workspace.xml
├── babel.config.js
├── database
├── forms
│ ├── index.ts
│ ├── user_packsack.ts
│ ├── switch_commands.ts
│ ├── suggestions.ts
│ ├── groups_config.ts
│ └── interface.ts
├── interface.ts
├── index.ts
└── tools.ts
├── app.ts
├── timing
├── index.ts
└── clearCurMaxScore.ts
├── package.json
└── README.md
/temporary/data.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/runtime/child.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * JavaScript
3 | */
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # 忽略所有node_modules文件夹
2 | node_modules/
3 | # 仅忽略项目根目录下的xiaoqiu文件夹
4 | /xiaoqiu
5 | /lib/user
--------------------------------------------------------------------------------
/assets/images/emoji/比心2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/emoji/比心2.jpg
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/runtime/index.ts:
--------------------------------------------------------------------------------
1 | import { runtime } from './runtime'
2 |
3 | export { runtime }
4 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/setCard/index.ts:
--------------------------------------------------------------------------------
1 | import { setCard } from './setCard'
2 |
3 | export { setCard }
4 |
--------------------------------------------------------------------------------
/assets/images/seeCard/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/seeCard/default.jpg
--------------------------------------------------------------------------------
/assets/readme/darks/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/readme/darks/default.jpg
--------------------------------------------------------------------------------
/assets/readme/lights/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/readme/lights/default.jpg
--------------------------------------------------------------------------------
/assets/videos/listen/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/videos/listen/default.jpg
--------------------------------------------------------------------------------
/assets/videos/other/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/videos/other/default.jpg
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/search/index.ts:
--------------------------------------------------------------------------------
1 | import { searchJueJin } from './search'
2 |
3 | export { searchJueJin }
4 |
--------------------------------------------------------------------------------
/temporary/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 机器人开机时的临时任务,大多用于开发环境中,例如统计数据等
3 | */
4 |
5 | const t = () => {}
6 |
7 | export { t }
8 |
--------------------------------------------------------------------------------
/assets/images/emoji/万用表情/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/emoji/万用表情/default.jpg
--------------------------------------------------------------------------------
/assets/images/emoji/代码相关/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/emoji/代码相关/default.jpg
--------------------------------------------------------------------------------
/assets/images/emoji/群聊相关/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/emoji/群聊相关/default.jpg
--------------------------------------------------------------------------------
/assets/images/groupchat/第一届线上前端交流会.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/groupchat/第一届线上前端交流会.jpg
--------------------------------------------------------------------------------
/lib/groupchat/interface.ts:
--------------------------------------------------------------------------------
1 | type MemberName = {
2 | nickname: string
3 | card: string
4 | }
5 |
6 | export { MemberName }
7 |
--------------------------------------------------------------------------------
/assets/images/groupchat/dark-menu-jfwf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/groupchat/dark-menu-jfwf.png
--------------------------------------------------------------------------------
/assets/images/groupchat/dark-menu-qglcz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/groupchat/dark-menu-qglcz.png
--------------------------------------------------------------------------------
/assets/images/groupchat/dark-menu-qlxg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/groupchat/dark-menu-qlxg.png
--------------------------------------------------------------------------------
/assets/images/groupchat/dark-menu-qtwf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/groupchat/dark-menu-qtwf.png
--------------------------------------------------------------------------------
/assets/images/groupchat/dark-menu-xqxg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/groupchat/dark-menu-xqxg.png
--------------------------------------------------------------------------------
/assets/images/groupchat/dark-version-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/groupchat/dark-version-1.png
--------------------------------------------------------------------------------
/assets/images/xiaoqiu/浅色模式/其它玩法/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/xiaoqiu/浅色模式/其它玩法/default.jpg
--------------------------------------------------------------------------------
/assets/images/xiaoqiu/浅色模式/小秋相关/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/xiaoqiu/浅色模式/小秋相关/default.jpg
--------------------------------------------------------------------------------
/assets/images/xiaoqiu/浅色模式/积分玩法/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/xiaoqiu/浅色模式/积分玩法/default.jpg
--------------------------------------------------------------------------------
/assets/images/xiaoqiu/浅色模式/群聊相关/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/xiaoqiu/浅色模式/群聊相关/default.jpg
--------------------------------------------------------------------------------
/assets/images/xiaoqiu/深色模式/其它玩法/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/xiaoqiu/深色模式/其它玩法/default.jpg
--------------------------------------------------------------------------------
/assets/images/xiaoqiu/深色模式/小秋相关/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/xiaoqiu/深色模式/小秋相关/default.jpg
--------------------------------------------------------------------------------
/assets/images/xiaoqiu/深色模式/积分玩法/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/xiaoqiu/深色模式/积分玩法/default.jpg
--------------------------------------------------------------------------------
/assets/images/xiaoqiu/深色模式/群聊相关/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/xiaoqiu/深色模式/群聊相关/default.jpg
--------------------------------------------------------------------------------
/assets/images/groupchat/lights-menu-jfwf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/groupchat/lights-menu-jfwf.png
--------------------------------------------------------------------------------
/assets/images/groupchat/lights-menu-qglcz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/groupchat/lights-menu-qglcz.png
--------------------------------------------------------------------------------
/assets/images/groupchat/lights-menu-qlxg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/groupchat/lights-menu-qlxg.png
--------------------------------------------------------------------------------
/assets/images/groupchat/lights-menu-qtwf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/groupchat/lights-menu-qtwf.png
--------------------------------------------------------------------------------
/assets/images/groupchat/lights-menu-xqxg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/groupchat/lights-menu-xqxg.png
--------------------------------------------------------------------------------
/assets/images/groupchat/lights-version-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/groupchat/lights-version-1.png
--------------------------------------------------------------------------------
/assets/images/xiaoqiu/浅色模式/群管理操作/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/xiaoqiu/浅色模式/群管理操作/default.jpg
--------------------------------------------------------------------------------
/assets/images/xiaoqiu/深色模式/群管理操作/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/777miss/xiao-qiu/HEAD/assets/images/xiaoqiu/深色模式/群管理操作/default.jpg
--------------------------------------------------------------------------------
/lib/time/interface.ts:
--------------------------------------------------------------------------------
1 | type UnitEng = 'mins' | 'hours' | 'hours'
2 | type UnitChi = '分钟' | '小时' | '天'
3 |
4 | export {
5 | UnitEng,
6 | UnitChi
7 | }
8 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/draw/index.ts:
--------------------------------------------------------------------------------
1 | import { draw } from './draw'
2 | import { drawCount } from './drawCount'
3 |
4 | export {
5 | draw,
6 | drawCount
7 | }
8 |
--------------------------------------------------------------------------------
/eventsHandle/groupchat/index.ts:
--------------------------------------------------------------------------------
1 | import { askQuestions } from './askQuestions'
2 | import { signIn } from './signIn'
3 |
4 | export {
5 | askQuestions,
6 | signIn
7 | }
8 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/packsack/index.ts:
--------------------------------------------------------------------------------
1 | import { getPacksack } from './getPacksack'
2 | import { addScore } from './addScore'
3 |
4 | export {
5 | getPacksack,
6 | addScore
7 | }
8 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/propCard/index.ts:
--------------------------------------------------------------------------------
1 | import { banAndDelCard } from './banAndDelCard'
2 | import { seeCard } from './seeCard'
3 |
4 | export {
5 | banAndDelCard,
6 | seeCard
7 | }
8 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/eventsHandle/proxy/index.ts:
--------------------------------------------------------------------------------
1 | import { verifyAnswer } from './verifyAnswer'
2 | import { verifyAllowedSpeak } from './verifyAllowedSpeak'
3 |
4 | export {
5 | verifyAnswer,
6 | verifyAllowedSpeak
7 | }
8 |
--------------------------------------------------------------------------------
/lib/methods/types.ts:
--------------------------------------------------------------------------------
1 | type BasisType = string | number | boolean
2 | interface TupleType {
3 | (...args: T): T
4 | }
5 | const tuple: TupleType = (...args) => args
6 |
7 | export { tuple }
8 |
--------------------------------------------------------------------------------
/lib/interface/methods.ts:
--------------------------------------------------------------------------------
1 | import { self } from '../user'
2 |
3 | // 转发聊天记录
4 | type ForwardRecord = {
5 | user_id: typeof self.uin
6 | nickname: string
7 | message: string
8 | }
9 |
10 | export { ForwardRecord }
11 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/checkDeadPerson/index.ts:
--------------------------------------------------------------------------------
1 | import { checkDeadPerson } from './checkDeadPerson'
2 | import { relieveCheckDeadPerson } from './relieveCheckDeadPerson'
3 |
4 | export {
5 | checkDeadPerson,
6 | relieveCheckDeadPerson
7 | }
8 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/notAllowedSpeak/index.ts:
--------------------------------------------------------------------------------
1 | import { notAllowedSpeak } from './notAllowedSpeak'
2 | import { relieveNotAllowedSpeak } from './relieveNotAllowedSpeak'
3 |
4 | export {
5 | notAllowedSpeak,
6 | relieveNotAllowedSpeak
7 | }
8 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['@babel/typescript'],
3 | plugins: [
4 | '@babel/plugin-transform-modules-commonjs',
5 | '@babel/proposal-class-properties',
6 | '@babel/proposal-object-rest-spread'
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/lib/user-copy/interface.ts:
--------------------------------------------------------------------------------
1 | type GroupchatConfigArrs = [T] extends V ? T : never
2 |
3 | type InAuthId = T extends { [propsName: string]: infer V } ? V[] : never
4 | type AuthIdInType = InAuthId
5 |
6 | export {
7 | GroupchatConfigArrs,
8 | AuthIdInType
9 | }
10 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/eventsHandle/xiaoqiu/index.ts:
--------------------------------------------------------------------------------
1 | import { isXiaoQiuOnline } from './isXiaoQiuOnline'
2 | import { menu } from './menu'
3 | import { switchCommand } from './switchCommand'
4 | import { versions } from './versions'
5 |
6 | export {
7 | isXiaoQiuOnline,
8 | menu,
9 | switchCommand,
10 | versions
11 | }
12 |
--------------------------------------------------------------------------------
/lib/oicq/index.ts:
--------------------------------------------------------------------------------
1 | import { menu, versions } from './menuKeywords'
2 | import { bot, segment, operations } from './oicqOperations'
3 | import { registerEvents } from './registerEvents'
4 |
5 | export {
6 | menu,
7 | versions,
8 | bot,
9 | segment,
10 | operations,
11 | registerEvents
12 | }
13 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/checkDeadPerson/interface.ts:
--------------------------------------------------------------------------------
1 | type TopicInfo = {
2 | topic: string
3 | answer: string[]
4 | times: number
5 | isFailed: boolean
6 | }
7 | type CheckDeadPerson = {
8 | token: NodeJS.Timeout
9 | } & TopicInfo
10 |
11 | export {
12 | TopicInfo,
13 | CheckDeadPerson
14 | }
15 |
--------------------------------------------------------------------------------
/database/forms/index.ts:
--------------------------------------------------------------------------------
1 | import { groups_config } from './groups_config'
2 | import { suggestions } from './suggestions'
3 | import { switch_commands } from './switch_commands'
4 | import { user_packsack } from './user_packsack'
5 |
6 | export {
7 | groups_config,
8 | suggestions,
9 | switch_commands,
10 | user_packsack
11 | }
12 |
--------------------------------------------------------------------------------
/app.ts:
--------------------------------------------------------------------------------
1 | import { timing } from './timing'
2 | import { registerEvents } from './lib/oicq'
3 | import { t } from './temporary'
4 | import { groupchatsId } from './lib/user'
5 | import { toInitFormData } from './database/tools'
6 |
7 | // 临时任务
8 | t()
9 | // 定时任务
10 | timing()
11 | // 初始化数据
12 | groupchatsId.forEach(id => toInitFormData(id))
13 | // 注册事件
14 | registerEvents()
15 |
--------------------------------------------------------------------------------
/lib/user-copy/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | self,
3 | groupchatsId,
4 | autoGroupchatsName,
5 | connectionDbConfig,
6 | AuthId,
7 | AuthIdType,
8 | GroupsId
9 | } from './account-alpha'
10 |
11 | export {
12 | self,
13 | groupchatsId,
14 | autoGroupchatsName,
15 | connectionDbConfig,
16 | AuthId,
17 | AuthIdType,
18 | GroupsId
19 | }
20 |
--------------------------------------------------------------------------------
/lib/methods/createImg.ts:
--------------------------------------------------------------------------------
1 | import images from 'images'
2 |
3 | type CreateImg = (urls: { init: string; save: string }) => void
4 | // 以指定图片为样本,生成一张它的副本
5 | const createImg: CreateImg = urls => {
6 | const { init, save } = urls
7 | const width = images(init).width()
8 | const height = images(init).height()
9 | images(width, height).draw(images(init), 0, 0).save(save)
10 | }
11 |
12 | export { createImg }
13 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/notAllowedSpeak/tools.ts:
--------------------------------------------------------------------------------
1 | import type { GroupchatsId } from '../../../lib/interface'
2 |
3 | // 存储当前正处于禁止发言期间的成员
4 | const persons = new Map([])
5 | // 检测某位成员是否已经处于禁止发言期间
6 | const judgeMemberNotSpeak = (gId: GroupchatsId, uId: number) => {
7 | const id = String(gId) + String(uId)
8 | return !!persons.get(id)
9 | }
10 |
11 | export {
12 | persons,
13 | judgeMemberNotSpeak
14 | }
15 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/draw/interface.ts:
--------------------------------------------------------------------------------
1 | type CardResult = {
2 | ban: number
3 | delMsg: number
4 | immune: number
5 | see: number
6 | }
7 | type AllResult = {
8 | score: number
9 | card: CardResult
10 | }
11 | type LucklyResult = {
12 | all: AllResult
13 | advance: {
14 | explain: string
15 | }
16 | }
17 |
18 | export {
19 | CardResult,
20 | AllResult,
21 | LucklyResult
22 | }
23 |
--------------------------------------------------------------------------------
/eventsHandle/proxy/interface.ts:
--------------------------------------------------------------------------------
1 | import type { GroupchatsId } from '../../lib/interface'
2 | import type { OicqOperations } from '../../lib/oicq/interface'
3 |
4 | // 禁止发言
5 | type ProxyArgs = {
6 | bot: any
7 | gId: GroupchatsId
8 | uId: number
9 | operations: OicqOperations
10 | raw_message: string
11 | }
12 | type VerifyProxy = (info: ProxyArgs) => boolean
13 |
14 | export {
15 | ProxyArgs,
16 | VerifyProxy
17 | }
18 |
--------------------------------------------------------------------------------
/lib/groupchat/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | sexArr,
3 | sexBelong,
4 | areaAll,
5 | areaAbbr,
6 | keywordsReg,
7 | randomProvince,
8 | nicknameFormCard,
9 | formSpecificProvinceToShort
10 | } from './groupNickname'
11 |
12 | export {
13 | sexArr,
14 | sexBelong,
15 | areaAll,
16 | areaAbbr,
17 | keywordsReg,
18 | randomProvince,
19 | nicknameFormCard,
20 | formSpecificProvinceToShort
21 | }
22 |
--------------------------------------------------------------------------------
/lib/Temp/temp.ts:
--------------------------------------------------------------------------------
1 | import type { SendContent, CommandFn } from '../interface'
2 |
3 | const sendContent: SendContent = {
4 | name: '小秋你好',
5 | reg: /reg/gi,
6 | role: 'member',
7 | member: () => [],
8 | admin: () => [],
9 | owner: () => [],
10 | deverDefined: () => [[], []],
11 | equal: () => [],
12 | level: () => []
13 | }
14 | const fn: CommandFn = originData => { }
15 | const commandFileName = { fn, sendContent }
16 |
17 | export { commandFileName }
18 |
--------------------------------------------------------------------------------
/eventsHandle/proxy/verifyAllowedSpeak.ts:
--------------------------------------------------------------------------------
1 | import { judgeMemberNotSpeak } from '../groupchatAdmin/notAllowedSpeak/tools'
2 |
3 | import type { VerifyProxy } from './interface'
4 |
5 | const verifyAllowedSpeak: VerifyProxy = info => {
6 | const { gId, uId, operations: { delMsg } } = info
7 | const isHave = judgeMemberNotSpeak(gId, uId)
8 | if (!isHave) return false
9 | // 处于禁止发言期间,直接撤回消息
10 | delMsg(gId, uId, 1)
11 | return true
12 | }
13 |
14 | export { verifyAllowedSpeak }
15 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/checkDeadPerson/tools.ts:
--------------------------------------------------------------------------------
1 | import type { GroupchatsId } from '../../../lib/interface'
2 | import type { CheckDeadPerson } from './interface'
3 |
4 | // 存储当前人机验证的成员(包含其所对应的题目及答案)
5 | const persons = new Map([])
6 | // 判断某位成员是否已经处于人机验证环节中
7 | const inCheckDeadOf = (gId: GroupchatsId, oId: number) => {
8 | const id = String(gId) + String(oId)
9 | return !!persons.get(id)
10 | }
11 |
12 | export {
13 | persons,
14 | inCheckDeadOf
15 | }
16 |
--------------------------------------------------------------------------------
/.idea/bot-master.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/lib/interface/account.ts:
--------------------------------------------------------------------------------
1 | import { AuthId, GroupsId } from '../user'
2 |
3 | // 小秋的账号与密码
4 | type BotAccount = {
5 | readonly uin: number
6 | readonly pwd: string
7 | }
8 | // 要监听群聊的ID、要修改群昵称的群聊ID
9 | type GroupchatsId = GroupsId['groupchatsId']
10 | type AutoGroupchatsName = GroupsId['autoGroupchatsName']
11 | // 小秋的群昵称格式
12 | type VersionsRule = `小秋 | Release v${number}.${number}.${number}`
13 |
14 | export {
15 | AuthId,
16 | BotAccount,
17 | GroupchatsId,
18 | AutoGroupchatsName,
19 | VersionsRule
20 | }
21 |
--------------------------------------------------------------------------------
/lib/interface/toolsType.ts:
--------------------------------------------------------------------------------
1 | type GetObject = {
2 | [k: string]: T
3 | }
4 | type ChoiceProps = {
5 | [k in keyof T]?: T[k]
6 | }
7 | type TransformStringFormat = T extends `${infer P}_${infer N}`
8 | ? `${P}${Capitalize}`
9 | : never
10 | type UnderlineTransformHumpKeys = {
11 | [k in keyof object as k extends string ? TransformStringFormat : never]: T[k]
12 | }
13 |
14 | export {
15 | GetObject,
16 | ChoiceProps,
17 | UnderlineTransformHumpKeys
18 | }
19 |
--------------------------------------------------------------------------------
/timing/index.ts:
--------------------------------------------------------------------------------
1 | import { bot, versions } from '../lib/oicq'
2 | import { clearCurMaxScore } from './clearCurMaxScore'
3 | import { self, groupchatsId, autoGroupchatsName } from '../lib/user'
4 |
5 | const { uin } = self
6 | // 定时任务
7 | const timing = () => {
8 | // 每次启动时,5分钟后自动将群昵称修改为当前版本的版本号
9 | setTimeout(
10 | () => autoGroupchatsName.forEach(v => bot.setGroupCard(v, uin, versions)),
11 | 1000 * 60 * 5
12 | )
13 | // 凌晨12点自动清空每日积分获取上限
14 | clearCurMaxScore(groupchatsId)
15 | }
16 |
17 | export { timing }
18 |
--------------------------------------------------------------------------------
/eventsHandle/xiaoqiu/tools.ts:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 |
3 | const groupchatUrl = './assets/images/groupchat'
4 | // 获取对应模式下的菜单
5 | // true代表浅色,false代表深色
6 | const getModeImg = (classify: string[][], type: string, mode: boolean) => {
7 | // arr为一维数组,例如[init, init]
8 | // init代表每个模块所对应的路径
9 | const arr: string[][] = []
10 | const curMode = mode ? 'lights' : 'dark'
11 | classify.forEach(v => arr.push([path.resolve(`${groupchatUrl}/${curMode}-${type}-${v[0]}.png`), v[1]]))
12 | return arr
13 | }
14 |
15 | export { getModeImg }
16 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/index.ts:
--------------------------------------------------------------------------------
1 | import { changeDrawDiscount } from './changeDrawDiscount'
2 | import { draw, drawCount } from './draw'
3 | import { getDrawDiscount } from './getDrawDiscount'
4 | import { getScoreWay } from './getScoreWay'
5 | import { getPacksack, addScore } from './packsack'
6 | import { banAndDelCard, seeCard } from './propCard'
7 |
8 | export {
9 | changeDrawDiscount,
10 | draw,
11 | drawCount,
12 | getDrawDiscount,
13 | getPacksack,
14 | addScore,
15 | banAndDelCard,
16 | seeCard,
17 | getScoreWay
18 | }
19 |
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/interface.ts:
--------------------------------------------------------------------------------
1 | // 文案指令
2 | type YiYanApi = {
3 | from: string
4 | from_who: string
5 | hitokoto: string
6 | }
7 | // 听歌指令
8 | type MusicResCode = {
9 | code: number
10 | }
11 | type MusicId = MusicResCode & {
12 | result: {
13 | songs: [
14 | {
15 | id: string
16 | }
17 | ]
18 | }
19 | }
20 | type MusicUrl = MusicResCode & {
21 | data: [
22 | {
23 | url: string
24 | }
25 | ]
26 | }
27 |
28 | export {
29 | YiYanApi,
30 | MusicId,
31 | MusicUrl
32 | }
33 |
--------------------------------------------------------------------------------
/lib/time/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | belong,
3 | secToFormat,
4 | getDate,
5 | getWholeDate,
6 | getCurTime,
7 | getCurTimeAlias,
8 | getAssignTimestamp,
9 | transformTimeNameAlias,
10 | transformTimeNameEn,
11 | getSpecificTimeMS,
12 | getCurDarkOrLights
13 | } from './timeUnitTransform'
14 |
15 | export {
16 | belong,
17 | secToFormat,
18 | getDate,
19 | getWholeDate,
20 | getCurTime,
21 | getCurTimeAlias,
22 | getAssignTimestamp,
23 | transformTimeNameAlias,
24 | transformTimeNameEn,
25 | getSpecificTimeMS,
26 | getCurDarkOrLights
27 | }
28 |
--------------------------------------------------------------------------------
/lib/methods/index.ts:
--------------------------------------------------------------------------------
1 | import { createImg } from './createImg'
2 | import { pubsub } from './pubsub'
3 | import {
4 | getRandomScope,
5 | getRandom,
6 | returnOneOfContent,
7 | otherIdFromCq,
8 | disruptOrder,
9 | getRandomDigitNumberStr,
10 | isDecimals,
11 | formatNumCount
12 | } from './tools'
13 | import { tuple } from './types'
14 |
15 | export {
16 | createImg,
17 | pubsub,
18 | getRandomScope,
19 | getRandom,
20 | returnOneOfContent,
21 | otherIdFromCq,
22 | disruptOrder,
23 | getRandomDigitNumberStr,
24 | isDecimals,
25 | formatNumCount,
26 | tuple
27 | }
28 |
--------------------------------------------------------------------------------
/assets/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "xiaoqiu",
3 | "version": "2.0.0",
4 | "description": "",
5 | "main": "main.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "node main.js"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "combine-async-error": "^0.2.0",
15 | "images": "^3.2.4",
16 | "mysql": "^2.18.1",
17 | "oicq": "^1.13.1",
18 | "protobufjs": "^7.0.0",
19 | "request": "^2.88.2",
20 | "superagent": "^8.0.0"
21 | },
22 | "devDependencies": {}
23 | }
24 |
--------------------------------------------------------------------------------
/lib/oicq/menuKeywords.ts:
--------------------------------------------------------------------------------
1 | import { commandsConfig } from '../../eventsHandle/fns'
2 |
3 | import type { GroupsSwitchCommands } from '../../database/forms/interface'
4 | import type { VersionsRule } from '../interface'
5 | import type { CommandsConfig } from '../../eventsHandle/fns'
6 |
7 | type Commands = (keyof CommandsConfig)[]
8 |
9 | const versions: VersionsRule = '小秋 | Release v2.0.0'
10 | const menu = {} as GroupsSwitchCommands
11 | const commandsFnNames = Object.keys(commandsConfig) as Commands
12 | commandsFnNames.forEach(fnName => {
13 | const key = commandsConfig[fnName].sendContent.name
14 | menu[key] = true
15 | })
16 |
17 | export {
18 | menu,
19 | versions
20 | }
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "xiaoqiu",
3 | "version": "2.0.0",
4 | "description": "",
5 | "main": "babel.config.js",
6 | "directories": {
7 | "lib": "lib"
8 | },
9 | "scripts": {
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "author": "",
13 | "license": "ISC",
14 | "devDependencies": {
15 | "@types/node": "^18.7.3"
16 | },
17 | "dependencies": {
18 | "@babel/plugin-transform-modules-commonjs": "^7.18.6",
19 | "@types/images": "^3.2.1",
20 | "@types/mysql": "^2.15.21",
21 | "@types/request": "^2.48.8",
22 | "@types/superagent": "^4.1.15"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/getScoreWay.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [获取积分方式]指令:
3 | * 查询获取积分的途径有哪些
4 | */
5 | import type { SendContent, CommandFn } from '../../lib/interface'
6 |
7 | const way = `①通过每日签到\n[详见:菜单-群聊相关]\n\n②为小秋提出有效建议(15积分)\n[详见:菜单-群聊相关-提建议]\n\n③为小秋找出有效bug(30积分)\n[详见:菜单-群聊相关-提建议]`
8 | const sendContent: SendContent = {
9 | name: '获取积分方式',
10 | reg: [
11 | /^获取积分$/,
12 | /^获取积分方式$/
13 | ],
14 | role: 'member',
15 | deverDefined: () => [
16 | [
17 | `现有的积分获取方式为:\n${way}`,
18 | `小秋查询到的积分获取方式是:\n${way}`
19 | ]
20 | ]
21 | }
22 | const fn: CommandFn = () => ({ items: 1 })
23 | const getScoreWay = { fn, sendContent }
24 |
25 | export { getScoreWay }
26 |
--------------------------------------------------------------------------------
/lib/oicq/tools.ts:
--------------------------------------------------------------------------------
1 | // 使用数组结构替代数据库中存储的群聊消息
2 | import { groupchatsId } from '../user'
3 |
4 | import type { PreGroupchatMessage } from '../../database/forms/interface'
5 | import type { GroupchatsId } from '../interface'
6 |
7 | type CacheGroupMsg = {
8 | [k in GroupchatsId]: PreGroupchatMessage[]
9 | }
10 |
11 | const cacheGroupMsg = {} as CacheGroupMsg
12 | groupchatsId.forEach(id => (cacheGroupMsg[id] = []))
13 | // 只缓存最近100条消息
14 | const addCacheMsgRows = (groupId: GroupchatsId, content: PreGroupchatMessage) => {
15 | if (cacheGroupMsg[groupId].length >= 100) cacheGroupMsg[groupId].shift()
16 | cacheGroupMsg[groupId].push(content)
17 | }
18 |
19 | export {
20 | cacheGroupMsg,
21 | addCacheMsgRows
22 | }
23 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/index.ts:
--------------------------------------------------------------------------------
1 | import { alert } from './alert'
2 | import { banCommon } from './banCommon'
3 | import { checkDeadPerson, relieveCheckDeadPerson } from './checkDeadPerson'
4 | import { notAllowedSpeak, relieveNotAllowedSpeak } from './notAllowedSpeak'
5 | import { delMemberMessage } from './delMemberMessage'
6 | import { setCard } from './setCard'
7 | import { setGroupKick } from './setGroupKick'
8 | import { setGroupWholeBan } from './setGroupWholeBan'
9 |
10 | export {
11 | alert,
12 | banCommon,
13 | checkDeadPerson,
14 | relieveCheckDeadPerson,
15 | notAllowedSpeak,
16 | relieveNotAllowedSpeak,
17 | delMemberMessage,
18 | setCard,
19 | setGroupKick,
20 | setGroupWholeBan
21 | }
22 |
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/index.ts:
--------------------------------------------------------------------------------
1 | import { runtime } from './runtime'
2 | import { getCopyWriting } from './getCopyWriting'
3 | import { goodMorning } from './goodMorning'
4 | import { goodNight } from './goodNight'
5 | import { getSuggestions } from './getSuggestions'
6 | import { landlords } from './landlords'
7 | import { listenMusic } from './listenMusic'
8 | import { makeSuggestion } from './makeSuggestion'
9 | import { xiaoqiuChat } from './xiaoqiuChat'
10 | import { searchJueJin } from './search'
11 |
12 | export {
13 | runtime,
14 | getCopyWriting,
15 | goodNight,
16 | getSuggestions,
17 | landlords,
18 | goodMorning,
19 | listenMusic,
20 | makeSuggestion,
21 | xiaoqiuChat,
22 | searchJueJin
23 | }
24 |
--------------------------------------------------------------------------------
/eventsHandle/arguments/menuHelp.ts:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 |
3 | import { getCurDarkOrLights } from '../../lib/time'
4 | import { returnOneOfContent } from '../../lib/methods'
5 |
6 | import type { OicqOperations } from '../../lib/oicq/interface'
7 |
8 | const lightsCommandUrl = './assets/images/xiaoqiu/浅色模式/小秋相关/0.jpg'
9 | const darkCommandUrl = './assets/images/xiaoqiu/深色模式/小秋相关/0.jpg'
10 |
11 | const menuUseCommandImgUrl = getCurDarkOrLights() ? lightsCommandUrl : darkCommandUrl
12 | const getMenuHelp = (promiseImage: OicqOperations['promiseImage']) =>
13 | returnOneOfContent([
14 | `哎呀 不要来烦小秋哦~,有事看[菜单]`,
15 | `烦死了 别艾特我 有事看[菜单]哦~`,
16 | `小秋给您准备了[菜单]指令哦~\n${promiseImage(path.resolve(menuUseCommandImgUrl))}`,
17 | `艾特回去`
18 | ])
19 |
20 | export { getMenuHelp }
21 |
--------------------------------------------------------------------------------
/lib/interface/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AuthId,
3 | BotAccount,
4 | GroupchatsId,
5 | AutoGroupchatsName,
6 | VersionsRule
7 | } from './account'
8 | import {
9 | Role,
10 | LowestRole,
11 | GroupAt,
12 | MemberBasisInfo,
13 | OriginMemberBasisInfo,
14 | SendContent,
15 | GroupBasisInfo,
16 | InfoTemp,
17 | OriginData,
18 | CommandFn
19 | } from './command'
20 | import { ForwardRecord } from './methods'
21 |
22 | export {
23 | AuthId,
24 | BotAccount,
25 | GroupchatsId,
26 | AutoGroupchatsName,
27 | VersionsRule,
28 |
29 | Role,
30 | LowestRole,
31 | GroupAt,
32 | MemberBasisInfo,
33 | OriginMemberBasisInfo,
34 | SendContent,
35 | GroupBasisInfo,
36 | InfoTemp,
37 | OriginData,
38 | CommandFn,
39 | ForwardRecord
40 | }
41 |
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/search/interface.ts:
--------------------------------------------------------------------------------
1 | type Articles = {
2 | flag: boolean
3 | article: string[]
4 | }
5 | type Model = {
6 | result_model: {
7 | article_info: {
8 | title: string
9 | article_id: string
10 | brief_content: string
11 | view_count: number
12 | digg_count: number
13 | comment_count: number
14 | }
15 | author_user_info: {
16 | user_name: string
17 | }
18 | }
19 | }
20 | type JueJinSearch = {
21 | data: Model[]
22 | err_no: number
23 | }
24 | type ArticleTemp = (
25 | id: string,
26 | title: string,
27 | abstract: string,
28 | author: string,
29 | viewCounts: number,
30 | likeCounts: number,
31 | commentCounts: number
32 | ) => string
33 |
34 | export {
35 | Articles,
36 | Model,
37 | JueJinSearch,
38 | ArticleTemp
39 | }
40 |
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/landlords.ts:
--------------------------------------------------------------------------------
1 | /**、
2 | * [斗地主]指令:
3 | * 发送指定关键词后,小秋会随机发送一条有关斗地主的语句,仅作为一种娱乐玩法而存在
4 | */
5 | import type { SendContent, CommandFn } from '../../lib/interface'
6 |
7 | const arr = [
8 | '十七张牌,你能秒我?',
9 | '得得得得得得得得得得',
10 | '搞快点 搞快点',
11 | '这四百万是我最后的倔强',
12 | '心态崩了呀',
13 | '就这?',
14 | '这谁顶得住啊',
15 | '顶不住也要顶',
16 | '我能怎么办,我也很绝望啊',
17 | '快点啊,等到我花儿都谢了',
18 | '你的牌打的也太好了',
19 | '你是MM还是GG',
20 | '大家好,很高兴见到各位',
21 | '不要走,决战到天亮',
22 | '不好意思,我要离开一会',
23 | '嘿嘿 这局小秋一定会赢~'
24 | ]
25 |
26 | const sendContent: SendContent = {
27 | name: '斗地主',
28 | reg: /^((王炸)|(炸弹)|(单牌)|(对牌)|(三张牌)|(三带一)|(三带二)|(三带一对)|(单顺)|(双顺)|(三顺)|(飞机带翅膀)|(飞机))$/,
29 | role: 'member',
30 | member: () => arr,
31 | admin: () => arr,
32 | owner: () => arr
33 | }
34 | const fn: CommandFn = () => { }
35 | const landlords = { fn, sendContent }
36 |
37 | export { landlords }
38 |
--------------------------------------------------------------------------------
/lib/user-copy/account.ts:
--------------------------------------------------------------------------------
1 | // 正式版配置
2 | import { tuple } from '../methods/types'
3 |
4 | import type { AuthIdInType } from './interface'
5 | import type { BotAccount } from '../interface'
6 |
7 | // 账号与密码
8 | const self: BotAccount = {
9 | uin: 111111,
10 | pwd: '111111'
11 | }
12 | const g1 = 222
13 | // 要监听的群聊ID
14 | const groupchatsId = tuple(g1)
15 | // 要修改群昵称的群聊ID
16 | const autoGroupchatsName = tuple(g1)
17 | // 数据库配置
18 | const connectionDbConfig = {
19 | host: '',
20 | user: '',
21 | password: '',
22 | database: ''
23 | }
24 | // 绝对权限(特定用户)
25 | const AuthId = {
26 | FuncJin: 333
27 | } as const
28 | type AuthIdType = AuthIdInType
29 | interface GroupsId {
30 | groupchatsId: typeof g1
31 | autoGroupchatsName: typeof g1
32 | }
33 |
34 | export {
35 | self,
36 | groupchatsId,
37 | autoGroupchatsName,
38 | connectionDbConfig,
39 | AuthId,
40 | AuthIdType,
41 | GroupsId
42 | }
43 |
--------------------------------------------------------------------------------
/lib/user-copy/account-alpha.ts:
--------------------------------------------------------------------------------
1 | // 测试版配置
2 | import { tuple } from '../methods/types'
3 |
4 | import type { AuthIdInType } from './interface'
5 | import type { BotAccount } from '../interface'
6 |
7 | // 账号与密码
8 | const self: BotAccount = {
9 | uin: 111111,
10 | pwd: '111111'
11 | }
12 | const g1 = 222
13 | // 要监听的群聊ID
14 | const groupchatsId = tuple(g1)
15 | // 要修改群昵称的群聊ID
16 | const autoGroupchatsName = tuple(g1)
17 | // 数据库配置
18 | const connectionDbConfig = {
19 | host: '',
20 | user: '',
21 | password: '',
22 | database: ''
23 | }
24 | // 绝对权限(特定用户)
25 | const AuthId = {
26 | FuncJin: 333
27 | } as const
28 | type AuthIdType = AuthIdInType
29 | interface GroupsId {
30 | groupchatsId: typeof g1
31 | autoGroupchatsName: typeof g1
32 | }
33 |
34 | export {
35 | self,
36 | groupchatsId,
37 | autoGroupchatsName,
38 | connectionDbConfig,
39 | AuthId,
40 | AuthIdType,
41 | GroupsId
42 | }
43 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/setCard/interface.ts:
--------------------------------------------------------------------------------
1 | import type { GroupchatsId, OriginData } from '../../../lib/interface'
2 |
3 | type Province = {
4 | province: '京'
5 | sex: string
6 | nickname: string
7 | }
8 | type Sex = 'male' | 'female' | 'unknown'
9 | type BeAtInfoGroupSex = {
10 | data: {
11 | sex: Sex
12 | card: string
13 | }
14 | }
15 | type BeAtInfoGroupCard = {
16 | data: {
17 | area: string
18 | sex: Sex
19 | card: string
20 | nickname: string
21 | }
22 | }
23 |
24 | type ChangeCard = (
25 | bot: OriginData['bot'],
26 | groupId: GroupchatsId,
27 | userId: number
28 | ) => Promise
29 | type IsCardRule = (
30 | bot: OriginData['bot'],
31 | groupId: GroupchatsId,
32 | userId: number
33 | ) => Promise<{ flag: boolean, reason: string }>
34 |
35 | export {
36 | Province,
37 | BeAtInfoGroupSex,
38 | BeAtInfoGroupCard,
39 | ChangeCard,
40 | IsCardRule
41 | }
--------------------------------------------------------------------------------
/lib/methods/pubsub.ts:
--------------------------------------------------------------------------------
1 | class PubSub {
2 | // map: [[eventName, fn]]
3 | container = new Map([])
4 | // 发布
5 | publish(name: string, data: unknown) {
6 | /**
7 | * 发布事件有两种情况:
8 | * 1. 当前事件已被订阅,则触发其事件
9 | * 2. 当前事件未被订阅,则抛出错误
10 | */
11 | const fn = this.container.get(name)
12 | if (!fn) throw new TypeError(`${name}事件未被订阅`)
13 | // 每个订阅的函数都会收到其发布的数据,及当前事件名
14 | const args = [data, name]
15 | fn(...args)
16 | }
17 | // 订阅
18 | subscribe(name: string, fn: Function) {
19 | this.container.set(name, fn)
20 | }
21 | // 取消订阅
22 | unsubscribe(name: string) {
23 | /**
24 | * 取消订阅有两种情况:
25 | * 1. 当前事件已被订阅过,则在container中删除该事件
26 | * 2. 当前事件未被订阅,则抛出错误
27 | */
28 | const have = this.container.get(name)
29 | if (!have) throw new TypeError(`${name}事件属于无效事件`)
30 | this.container.delete(name)
31 | }
32 | }
33 | const pubsub = new PubSub()
34 |
35 | export { pubsub }
36 |
--------------------------------------------------------------------------------
/database/interface.ts:
--------------------------------------------------------------------------------
1 | import { getDataBaseData } from './tools'
2 |
3 | // 每个指令至少接收到的操作数据库的方法
4 | type OperationsBasisSet = {
5 | recordRow: 'recordRow'
6 | updateData: 'updateData'
7 | retrieveData: 'retrieveData'
8 | }
9 | type ProcessOperationsSet = T & OperationsBasisSet
10 |
11 | type GroupsConfigOperationsSet = ProcessOperationsSet<{
12 | name: 'groups_config'
13 | }>
14 | type SuggestionsOperationsSet = ProcessOperationsSet<{
15 | name: 'suggestions'
16 | rowSuggestion: 'rowSuggestion'
17 | }>
18 | type SwitchCommandsOperationsSet = ProcessOperationsSet<{
19 | name: 'switch_commands'
20 | }>
21 | type UserPacksackOperationsSet = ProcessOperationsSet<{
22 | name: 'user_packsack'
23 | }>
24 |
25 | type DataBase = {
26 | getDataBaseData: typeof getDataBaseData
27 | formSet: {
28 | groups_config: GroupsConfigOperationsSet
29 | suggestions: SuggestionsOperationsSet
30 | switch_commands: SwitchCommandsOperationsSet
31 | user_packsack: UserPacksackOperationsSet
32 | }
33 | }
34 |
35 | export { DataBase }
36 |
--------------------------------------------------------------------------------
/timing/clearCurMaxScore.ts:
--------------------------------------------------------------------------------
1 | import { getDate, getWholeDate } from '../lib/time'
2 | import { getDataBaseData, formSet } from '../database'
3 | import { GroupchatsId } from '../lib/interface'
4 |
5 | const { user_packsack } = formSet
6 |
7 | // 凌晨12点清除积分上限
8 | const clearCurMaxScore = (groupchatsId: GroupchatsId[]) => {
9 | const time = new Date()
10 | const dur = 1000 * 60 * 60 * 24
11 | const curDateTemp = +new Date(getWholeDate(time))
12 | const nextDateTemp = +new Date(getDate(time)) + dur
13 | const clear = async () => {
14 | for (let i = 0, len = groupchatsId.length; i < len; i++) {
15 | const curGroupPacksack = await getDataBaseData(user_packsack.name, user_packsack.retrieveData)(groupchatsId[i])
16 | Object.keys(curGroupPacksack).forEach(me => (curGroupPacksack[me].curInc = 0))
17 | await getDataBaseData(user_packsack.name, user_packsack.updateData)(groupchatsId[i], curGroupPacksack)
18 | }
19 | setTimeout(clear, dur)
20 | }
21 | setTimeout(clear, nextDateTemp - curDateTemp)
22 | }
23 |
24 | export { clearCurMaxScore }
25 |
--------------------------------------------------------------------------------
/database/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | databaseSet,
3 | getDataBaseData,
4 | operationSql,
5 | toInitFormData
6 | } from './tools'
7 |
8 | import type { DataBase } from './interface'
9 |
10 | const basis = {
11 | recordRow: 'recordRow',
12 | updateData: 'updateData',
13 | retrieveData: 'retrieveData'
14 | } as const
15 | const basisGroupsConfig = {
16 | name: 'groups_config',
17 | ...basis
18 | } as const
19 | const basisSuggestions = {
20 | name: 'suggestions',
21 | rowSuggestion: 'rowSuggestion',
22 | ...basis
23 | } as const
24 | const basisSwitchCommands = {
25 | name: 'switch_commands',
26 | ...basis
27 | } as const
28 | const basisUserPacksack = {
29 | name: 'user_packsack',
30 | ...basis
31 | } as const
32 |
33 | // 用于在读取数据时获取对应的表名及操作
34 | const formSet: DataBase['formSet'] = {
35 | groups_config: basisGroupsConfig,
36 | suggestions: basisSuggestions,
37 | switch_commands: basisSwitchCommands,
38 | user_packsack: basisUserPacksack
39 | }
40 |
41 | export {
42 | databaseSet,
43 | getDataBaseData,
44 | operationSql,
45 | toInitFormData,
46 | formSet
47 | }
48 |
--------------------------------------------------------------------------------
/database/forms/user_packsack.ts:
--------------------------------------------------------------------------------
1 | import { operationSql } from '../tools'
2 |
3 | import type { GroupchatsId } from '../../lib/interface'
4 | import type { DataBaseDataType, GroupsUserPacksack } from './interface'
5 |
6 | type UserPacksackReocrd = Promise
7 | type UserPacksackUpdate = Promise
8 | type UserPacksackRetrieve = Promise
9 |
10 | const create = {
11 | recordRow: async (id: GroupchatsId): UserPacksackReocrd =>
12 | await operationSql(`insert into user_packsack(group_id,whole_user_id) values('${id}','{}')`)
13 | }
14 | const update = {
15 | updateData: async (id: GroupchatsId, wholeUserId: GroupsUserPacksack): UserPacksackUpdate =>
16 | await operationSql(`update user_packsack set whole_user_id='${JSON.stringify(wholeUserId)}' where group_id='${id}'`)
17 | }
18 | const retrieve = {
19 | retrieveData: async (id: GroupchatsId): UserPacksackRetrieve =>
20 | await operationSql(`select whole_user_id from user_packsack where group_id=${id}`)
21 | }
22 | const user_packsack = { ...create, ...update, ...retrieve }
23 |
24 | export { user_packsack }
25 |
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/goodMorning.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [早]指令:
3 | * 问候
4 | */
5 | import path from 'path'
6 |
7 | import type { SendContent, CommandFn } from '../../lib/interface'
8 |
9 | const sendContent: SendContent = {
10 | name: '早',
11 | reg: [
12 | /^早$/,
13 | /^早小秋$/,
14 | /^小秋早$/,
15 | /^小秋早安$/,
16 | /^早,小秋$/
17 | ],
18 | role: 'member',
19 | deverDefined: ({ user: { name }, defined: { img }, operations: { at } }) => [
20 | [
21 | `${at('user')} 早 小秋才刚睡醒哦~`,
22 | `早 ${name}`,
23 | `${name},早安`
24 | ],
25 | [
26 | `${at('user')} 不早了,干活吧${img}`,
27 | `${name},这还早啥???赶紧上号${img}`,
28 | `${at('user')} 不早了哦,一起来了解下React18新特性吧~${img}`
29 | ]
30 | ]
31 | }
32 | const fn: CommandFn = originData => {
33 | const { operations: { promiseImage } } = originData
34 | const hours = new Date().getHours()
35 | if (hours >= 6 && hours <= 10) return { items: 1 }
36 | const url = path.resolve('./assets/images/emoji/代码相关/goVScode.jpg')
37 | const img = promiseImage(url)
38 | return { items: 2, args: { img } }
39 | }
40 | const goodMorning = { fn, sendContent }
41 |
42 | export { goodMorning }
43 |
--------------------------------------------------------------------------------
/database/forms/switch_commands.ts:
--------------------------------------------------------------------------------
1 | import { menu } from '../../lib/oicq'
2 | import { operationSql } from '../tools'
3 |
4 | import type { GroupchatsId } from '../../lib/interface'
5 | import type { DataBaseDataType, GroupsSwitchCommands } from './interface'
6 |
7 | type SwitchCommandsRecord = Promise
8 | type SwitchCommandsUpdate = Promise
9 | type SwitchCommandsRetrieve = Promise
10 |
11 | const create = {
12 | recordRow: async (id: GroupchatsId): SwitchCommandsRecord =>
13 | await operationSql(`insert into switch_commands(group_id,commands) values('${id}','${JSON.stringify(menu)}')`)
14 | }
15 | const update = {
16 | updateData: async (id: GroupchatsId, commands: GroupsSwitchCommands): SwitchCommandsUpdate =>
17 | await operationSql(`update switch_commands set commands='${JSON.stringify(commands)}' where group_id='${id}'`)
18 | }
19 | const retrieve = {
20 | retrieveData: async (id: GroupchatsId): SwitchCommandsRetrieve =>
21 | await operationSql(`select commands from switch_commands where group_id=${id}`)
22 | }
23 | const switch_commands = { ...create, ...update, ...retrieve }
24 |
25 | export { switch_commands }
26 |
--------------------------------------------------------------------------------
/database/forms/suggestions.ts:
--------------------------------------------------------------------------------
1 | import { operationSql } from '../tools'
2 |
3 | import type { DataBaseDataType, SuggestionFormat } from './interface'
4 |
5 | type GroupsSuggestion = SuggestionFormat[]
6 | type SuggestionsRecord = Promise
7 | type SuggestionsUpdate = Promise
8 | type SuggestionsRowData = Promise
9 | type SuggestionsRetrieve = Promise
10 |
11 | const create = {
12 | recordRow: async (id: number, sug: GroupsSuggestion): SuggestionsRecord =>
13 | await operationSql(`insert into suggestions(user_id,suggestion) values('${id}','${JSON.stringify(sug)}')`)
14 | }
15 | const update = {
16 | updateData: async (id: number, sug: GroupsSuggestion): SuggestionsUpdate =>
17 | await operationSql(`update suggestions set suggestion='${JSON.stringify(sug)}' where user_id='${id}'`)
18 | }
19 | const retrieve = {
20 | rowSuggestion: async (id: number): SuggestionsRowData =>
21 | await operationSql(`select suggestion from suggestions where user_id=${id}`),
22 | retrieveData: async (): SuggestionsRetrieve =>
23 | await operationSql(`select user_id,suggestion from suggestions`, true)
24 | }
25 | const suggestions = { ...create, ...update, ...retrieve }
26 |
27 | export { suggestions }
28 |
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/goodNight.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [晚安]指令:
3 | * 问候
4 | */
5 | import request from 'request'
6 |
7 | import { getCurTime } from '../../lib/time'
8 |
9 | import type { SendContent, CommandFn } from '../../lib/interface'
10 |
11 | const sendContent: SendContent = {
12 | name: '晚',
13 | reg: [
14 | /^晚安小秋$/,
15 | /^小秋晚安$/,
16 | /^晚安$/,
17 | /^晚安兄弟们$/
18 | ],
19 | role: 'member',
20 | deverDefined: ({ user: { name, sex }, defined: { hours, mins, text }, operations: { at } }) => [
21 | [
22 | `${at('user')} 这??都${hours}:${mins}了,这个年纪你能睡得着吗?`,
23 | `${at('user')} ${hours}:${mins},这个年纪你睡得下吗?`,
24 | `${at('user')} ${sex ? '哥哥' : '姐姐'}先睡吧!${hours}:${mins}分,小秋这时候还在上班哦`
25 | ],
26 | [
27 | `${at('user')} ${text}`,
28 | `${name},${text}`
29 | ]
30 | ]
31 | }
32 | const fn: CommandFn = () =>
33 | new Promise(resolve => {
34 | const [hours, mins] = getCurTime().split(':')
35 | const nHours = Number(hours)
36 | if (nHours > 6 && nHours < 21) return resolve({ items: 1, args: { hours, mins } })
37 | request.get('https://api.lovelive.tools/api/SweetNothings', (err: unknown, req: unknown, text: string) => {
38 | resolve({ items: 2, args: { text } })
39 | })
40 | })
41 | const goodNight = { fn, sendContent }
42 |
43 | export { goodNight }
44 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/getDrawDiscount.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [查积分抽奖折扣]指令:
3 | * 查询当前群聊中积分抽奖池的折扣数及折扣时间
4 | */
5 | import { getWholeDate } from '../../lib/time'
6 | import { isDiscount } from './draw/tools'
7 |
8 | import type { SendContent, CommandFn } from '../../lib/interface'
9 |
10 | const sendContent: SendContent = {
11 | name: '查询限时折扣',
12 | reg: [
13 | /^查询积分抽奖折扣$/,
14 | /^查询积分抽奖池折扣$/,
15 | /^积分抽奖池折扣$/,
16 | /^积分抽奖折扣$/,
17 | /^抽奖折扣$/,
18 | /^积分折扣$/
19 | ],
20 | role: 'member',
21 | deverDefined: ({ operations: { at }, defined: { discount, lastTime } }) => [
22 | [
23 | `${at('user')} 小秋查询到当前尚不处于折扣期间哦~`,
24 | `${at('user')} 积分抽奖池当前未进行限时折扣`,
25 | `${at('user')} 小秋发现当前不属于折扣期间`
26 | ],
27 | [
28 | `${at('user')} 哇 现在正是奖池的折扣时间呀!${lastTime}前都是${discount}折哦~`,
29 | `${at('user')} 小秋听说${lastTime}前都是${discount}折呦`,
30 | `${at('user')} 这不巧了嘛 ${lastTime}前,积分抽奖池都是${discount}折的折扣哦~`
31 | ]
32 | ]
33 | }
34 | const fn: CommandFn = async originData => {
35 | const { group } = originData
36 | const { flag, discount, timestamp } = await isDiscount(group.id)
37 | if (!flag) return { items: 1 }
38 | const lastTime = getWholeDate(new Date(timestamp!))
39 | return { items: 2, args: { discount, lastTime } }
40 | }
41 | const getDrawDiscount = { fn, sendContent }
42 |
43 | export { getDrawDiscount }
44 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/setGroupKick.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [踢]指令:
3 | * 将指定成员从群聊中移除
4 | */
5 | import type { SendContent, CommandFn } from '../../lib/interface'
6 |
7 | const sendContent: SendContent = {
8 | name: '踢',
9 | reg: /^踢\[CQ:at,qq=(?\d*),text=.*\]\s*$/,
10 | role: 'admin',
11 | member: ({ user: { sex }, operations: { at } }) => [
12 | `${at('user')} ${sex ? '哥哥' : '姐姐'},等你当管理or成为群主就行了`,
13 | `${at('user')} 抱歉 您权限不足`,
14 | `${at('user')} 小秋检测到您暂无此权限`
15 | ],
16 | deverDefined: ({ other: { name }, operations: { at } }) => [
17 | [
18 | `${at('user')} 已撤回1小时内${name}发送的所有消息并将其踢出了群聊`,
19 | `${at('user')} 小秋已撤回1小时内${name}发送的所有消息并将其踢出了群聊`,
20 | `${at('user')} ${name}已被您撤回1小时内所有消息并被踢出了群聊`
21 | ]
22 | ],
23 | equal: ({ user: { name }, operations: { at } }) => [
24 | `${at('user')} 对不起 小秋无法帮您进行踢出`,
25 | `${at('other')} ${name}居然要把你踢出去`,
26 | `${at('user')} 小秋暂时无法帮您使用[踢]指令`
27 | ],
28 | level: ({ operations: { at } }) => [
29 | `${at('user')} 抱歉 小秋权限不足 暂时不能帮您执行踢出操作哦~`,
30 | `${at('user')} 呜呜 小秋权限不足 暂时不能帮您执行踢出操作哦~`
31 | ]
32 | }
33 | const fn: CommandFn = originData => {
34 | const { bot, group, other, operations } = originData
35 | operations.delMsg(group.id, other.id, 0)
36 | bot.setGroupKick(group.id, other.id)
37 | return { items: 1 }
38 | }
39 | const setGroupKick = { fn, sendContent }
40 |
41 | export { setGroupKick }
42 |
--------------------------------------------------------------------------------
/eventsHandle/groupchat/askQuestions.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [问题格式]指令:
3 | * 当某群成员的问题格式不是理想格式时,可以使用此指令来提醒群成员
4 | */
5 | import type { SendContent, CommandFn } from '../../lib/interface'
6 |
7 | const stop = '①遇到的问题是什么\n②出错的截图+代码\n③想实现的效果大概是?'
8 |
9 | const sendContent: SendContent = {
10 | name: '问题格式',
11 | reg: [
12 | /^问题格式$/,
13 | /^问问题的格式$/,
14 | /^问题方式$/,
15 | /^怎样请教$/,
16 | /^问题形式$/,
17 | /^问问题都不会吗$/
18 | ],
19 | role: 'member',
20 | member: ({ user: { name } }) => [
21 | `${name}温馨提示,向群友请教问题的基本步骤是:\n${stop}`,
22 | `${name}与小秋一致认为,向群友请教问题的基本步骤是:\n${stop}`,
23 | `哎呀 同志,问问题的时候应该要有三个基本步骤哦~\n${stop}`,
24 | `小秋认为请教问题的时候至少要符合以下三个基本步骤\n${stop}`,
25 | `你看这个步骤,它又完整又详细\n${stop}\n\n[听说在请教问题时,遵循以上步骤会有意想不到的收获]`,
26 | `请教问题的正确姿势~\n${stop}`
27 | ],
28 | admin: ({ user: { sex, name } }) => [
29 | `${name}以管理员的身份提醒群友,请教问题时至少要有以下格式:\n${stop}`,
30 | `${name}管理${sex ? '哥哥' : '姐姐'}觉得向群友请教问题的基本步骤必须要:\n${stop}`,
31 | `哎呀 宝贝,问问题的时候应该要有三个基本步骤哦~\n${stop}`,
32 | `${stop}\n\n[${name}与小秋打听到在请教问题时,遵循以上步骤会有意想不到的收获哦~]`,
33 | `请教问题的正确姿势(非ban必选)~\n${stop}`
34 | ],
35 | owner: ({ user: { sex, name } }) => [
36 | `群主${name}带着正确的请教步骤来了!\n${stop}`,
37 | `${name}群主${sex ? '哥哥' : '姐姐'}觉得向群友请教问题的基本步骤是:\n${stop}`,
38 | `群主为大家打听到了问问题的时候至少要有的三个基本步骤\n${stop}`,
39 | `家人们 群主发话了,问问题时必须要:\n${stop}`
40 | ]
41 | }
42 | const fn: CommandFn = () => { }
43 | const askQuestions = { fn, sendContent }
44 |
45 | export { askQuestions }
46 |
--------------------------------------------------------------------------------
/lib/Temp/temp-comment.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 每个指令都对应着sendContent配置对象与fn处理函数,其中
3 | * - sendContent标识该指令的触发方式、触发角色、返回的内容等
4 | * - fn则表示该指令会触发的处理函数(fn会收到固定参数originData,originData中包含了用到的所有基本信息)
5 | */
6 | // 限制sendContent与fn函数的形参、及其返回值
7 | import type { SendContent, CommandFn } from '../interface'
8 |
9 | // sendContent为一个配置对象
10 | const sendContent: SendContent = {
11 | // 当前指令的名称,用于开启/关闭当前指令
12 | name: '小秋你好',
13 | // reg代表触发该指令的正则。其值可以是一个正则表达式,也可以是一个由正则表达式组成的数组
14 | reg: /reg/gi,
15 | // role代表发起者应拥有怎样的角色才可以触发该指令。其值必须是一个最低角色权限(如果role的值为数字数组,则代表只有特定用户可以触发该指令)
16 | // 例如role: [123789456],代表只有账号为123789456的用户可以触发该指令
17 | role: 'member',
18 | /**
19 | * member函数、admin函数、owner函数、deverDefined函数、equal函数、level函数:
20 | * 以上函数的形参全部遵循infoTemp类型
21 | * 仅deverDefined函数的infoTemp中多了一项defined
22 | */
23 | // 当发起者是member时,会自动调用该函数。member必须返回一个字符串数组,当发送消息时,会从当前数组中随机抽取一条进行发送
24 | member: () => [],
25 | // 当发起者是admin时,会自动调用该函数。admin必须返回一个字符串数组,当发送消息时,会从当前数组中随机抽取一条进行发送
26 | admin: () => [],
27 | // 当发起者是owner时,会自动调用该函数。owner必须返回一个字符串数组,当发送消息时,会从当前数组中随机抽取一条进行发送
28 | owner: () => [],
29 | // 不管发起者是谁,只要处理函数fn中返回了{ items },则自动调用该函数。
30 | // 且deverDefined必须返回一个二维字符串数组,当发送消息时,会从当前数组(二维数组)中的一个随机数组中随机抽取一条进行发送
31 | deverDefined: () => [[], []],
32 | // 当机器人小秋与被执行人权限相同时,会自动触发该函数
33 | equal: () => [],
34 | // 表示当前指令对小秋的最低权限要求
35 | level: () => []
36 | }
37 | // 当前指令的事件处理函数
38 | const fn: CommandFn = originData => { }
39 | // 对其进行组合(指令的函数名称应当与所在文件的名称一致)
40 | const commandFileName = { fn, sendContent }
41 |
42 | export { commandFileName }
43 |
--------------------------------------------------------------------------------
/database/forms/groups_config.ts:
--------------------------------------------------------------------------------
1 | import { operationSql } from '../tools'
2 |
3 | import type { GroupsConfig, DataBaseDataType } from './interface'
4 | import type { GroupchatsId } from '../../lib/interface'
5 |
6 | type GroupsConfigRecord = Promise
7 | type GroupsConfigUpdate = Promise
8 | type GroupsConfigRetrieve = Promise
9 |
10 | const groupsConfigTemp: GroupsConfig = {
11 | draw: {
12 | discount: 5,
13 | timestamp: 0
14 | },
15 | setCard: {
16 | isAuto: true,
17 | content: '又有大佬来了,群地位-1'
18 | },
19 | events: {
20 | message: true,
21 | increase: true,
22 | ban: true,
23 | decrease: true,
24 | poke: true
25 | },
26 | score: {
27 | dailyLimit: 4
28 | }
29 | }
30 |
31 | const create = {
32 | recordRow: async (id: GroupchatsId): GroupsConfigRecord =>
33 | await operationSql(`insert into groups_config(group_id,config) values('${id}','${JSON.stringify(groupsConfigTemp)}')`)
34 | }
35 | const update = {
36 | updateData: async (id: GroupchatsId, config: GroupsConfig): GroupsConfigUpdate =>
37 | await operationSql(`update groups_config set config='${JSON.stringify(config)}' where group_id='${id}'`)
38 | }
39 | const retrieve = {
40 | retrieveData: async (id: GroupchatsId): GroupsConfigRetrieve =>
41 | await operationSql(`select config from groups_config where group_id=${id}`)
42 | }
43 | const groups_config = { ...create, ...update, ...retrieve }
44 |
45 | export { groups_config }
46 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/packsack/tools.ts:
--------------------------------------------------------------------------------
1 | import { getDataBaseData, formSet } from '../../../database'
2 |
3 | import type { CardResult } from '../draw/interface'
4 | import type { GroupchatsId } from '../../../lib/interface'
5 | import type { ChoiceProps } from '../../../lib/interface/toolsType'
6 | import type { UserPacksack } from '../../../database/forms/interface'
7 |
8 | type AllType = {
9 | score?: number
10 | card?: ChoiceProps
11 | curInc?: number
12 | }
13 |
14 | const { user_packsack } = formSet
15 |
16 | const temp: UserPacksack = { score: 0, card: { ban: 0, immune: 0, delMsg: 0, see: 0 }, curInc: 0 }
17 | const setPacksack = async (groupId: GroupchatsId, userId: number, all: AllType) => {
18 | const wholePacksack = await getDataBaseData(user_packsack.name, user_packsack.retrieveData)(groupId)
19 | const initUserPasksack = async () => {
20 | wholePacksack[userId] = { ...temp }
21 | await getDataBaseData(user_packsack.name, user_packsack.updateData)(groupId, wholePacksack)
22 | }
23 | // 判断当前用户是否开启了背包
24 | if (!wholePacksack[userId]) initUserPasksack()
25 | const changePacksack = (newPacksack: any, prePacksack: any) => {
26 | Object.keys(newPacksack).forEach(name => {
27 | if (newPacksack[name] instanceof Object) changePacksack(newPacksack[name], prePacksack[name])
28 | else prePacksack[name] = prePacksack[name] + newPacksack[name]
29 | })
30 | }
31 | // 修改背包数据
32 | changePacksack(all, wholePacksack[userId])
33 | await getDataBaseData(user_packsack.name, user_packsack.updateData)(groupId, wholePacksack)
34 | }
35 |
36 | export { setPacksack }
37 |
--------------------------------------------------------------------------------
/eventsHandle/xiaoqiu/versions.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [小秋版本介绍]指令:
3 | * 获取小秋的版本介绍。
4 | * (只会发送当前所处的主版本号的版本介绍,例如当前处于2.x.x,则只会发送2.0.0-3.0.0之间所经历的版本变化,不会发送1.x.x的版本变化)
5 | */
6 | import { getCurDarkOrLights } from '../../lib/time'
7 | import { getModeImg } from './tools'
8 | import { self } from '../../lib/user'
9 |
10 | import type { SendContent, CommandFn } from '../../lib/interface'
11 |
12 | const sendContent: SendContent = {
13 | name: '小秋版本介绍',
14 | reg: [
15 | /^小秋版本介绍$/,
16 | /^小秋多大了$/,
17 | /^小秋几岁了$/,
18 | /^小秋版本号$/,
19 | /^秋秋的版本介绍$/
20 | ],
21 | role: 'member',
22 | deverDefined: ({ operations: { at } }) => [
23 | [
24 | `这就是小秋的过往啦~`,
25 | `秋秋总共经历过以下版本哦`,
26 | `${at('user')} 想要再了解一下小秋吗`,
27 | `${at('user')} 这就是v2.x的全部内容啦`,
28 | `${at('user')} 遇到不懂的地方,记得向小秋发送[菜单]指令`,
29 | `${at('user')} 悄悄告诉你,小秋也希望当前版本没有bug哦·`
30 | ]
31 | ]
32 | }
33 | const classify = [['1', '1']]
34 | // 版本介绍使用旧图变新图的方式
35 | const fn: CommandFn = async originData => {
36 | const { bot, group, segment } = originData
37 | // 获取当前是白天还是黑天
38 | const flag = getCurDarkOrLights()
39 | const arr = getModeImg(classify, 'version', flag)
40 | // 拿到路径后,因菜单较长,所以使用转发聊天记录的形式发出
41 | const result = arr.map(([url]) => ({
42 | user_id: self.uin,
43 | nickname: '这就是小秋所经过的版本介绍了~',
44 | message: segment.image(url)
45 | }))
46 | const forward = await bot.makeForwardMsg([...result])
47 | bot.sendGroupMsg(group.id, segment.xml(forward.data.data.data))
48 | return { items: 1 }
49 | }
50 | const versions = { fn, sendContent }
51 |
52 | export { versions }
53 |
--------------------------------------------------------------------------------
/database/tools.ts:
--------------------------------------------------------------------------------
1 | import mysql from 'mysql'
2 |
3 | import { connectionDbConfig } from '../lib/user'
4 | import { suggestions, switch_commands, user_packsack, groups_config } from './forms'
5 |
6 | import type { GroupchatsId } from '../lib/interface'
7 |
8 | const databaseSet = {
9 | suggestions,
10 | switch_commands,
11 | user_packsack,
12 | groups_config
13 | }
14 | // 连接数据库
15 | const connection = mysql.createPool(connectionDbConfig)
16 | // 对数据库进行操作
17 | const operationSql = (sql: string, isWhole?: boolean): Promise =>
18 | new Promise((res, rej) =>
19 | connection.query(sql, (err: null | Error, data) => {
20 | // 抛出了错误
21 | if (err) {
22 | console.log('operationSql数据库操作异常:', err)
23 | return rej(err)
24 | }
25 | console.log('operationSql读取数据库数据', data)
26 | // 读取单条记录或全部记录(采用的sql语句大都为读取单条记录,所以此处对单条数据进行格式化)
27 | const obj = data[0]
28 | if (!obj) return res(obj) // 返回空数据
29 | const result = isWhole ? data : JSON.parse(obj[Object.keys(obj)[0]])
30 | res(result)
31 | })
32 | )
33 | // 根据数据表读取数据
34 | const getDataBaseData = <
35 | T extends keyof typeof databaseSet,
36 | K extends keyof typeof databaseSet[T]
37 | >(form: T, operation: K) => {
38 | const sqlFn = databaseSet[form][operation]
39 | return sqlFn
40 | }
41 | // 对需要进行初始化的数据表进行初始化
42 | const initForm = [groups_config, switch_commands, user_packsack]
43 | const toInitFormData = (id: GroupchatsId) =>
44 | initForm.forEach(async form => {
45 | const data = await form.retrieveData(id)
46 | if (data) return
47 | form.recordRow(id)
48 | })
49 |
50 | export { databaseSet, getDataBaseData, operationSql, toInitFormData }
51 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/packsack/addScore.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [发放积分]指令:
3 | * 为指定成员发放积分
4 | */
5 | import { setPacksack } from './tools'
6 |
7 | import { AuthId } from '../../../lib/interface'
8 | import type { SendContent, CommandFn } from '../../../lib/interface'
9 |
10 | const sendContent: SendContent = {
11 | name: '发放积分',
12 | reg: [
13 | /^发放(?\d*)积分\[CQ:at,qq=(?\d*),text=.*\]\s*$/,
14 | /^发放积分(?\d*)\[CQ:at,qq=(?\d*),text=.*\]\s*$/
15 | ],
16 | role: [AuthId.FuncJin],
17 | member: ({ user: { name }, operations: { at } }) => [
18 | `${at('user')} 此操作仅供特定用户使用`,
19 | `${at('user')} 您暂无此权限`,
20 | `${name},反了你了`
21 | ],
22 | admin: ({ operations: { at } }) => [
23 | `${at('user')} 就算你是管理也不行 此操作仅供特定用户使用`,
24 | `${at('user')} 抱歉 您无此权限~`
25 | ],
26 | owner: ({ operations: { at } }) => [
27 | `${at('user')} 听说就连群主也无法触发此指令哦!`,
28 | `${at('user')} 糟糕 居然是红灯[尊贵的群主大人,您暂时无法使用此指令]`
29 | ],
30 | deverDefined: ({ user: { name }, operations: { at }, defined: { score } }) => [
31 | [
32 | `${at('user')} 小秋提示您,积分发放范围必须在1~1000万之间`,
33 | `${at('user')} 积分发放范围必须在1~1000万之间`
34 | ],
35 | [
36 | `${at('other')} Hi ${name}为您发放${score}积分`,
37 | `${at('other')} 人品大爆发 ${name}为您发放${score}积分`
38 | ]
39 | ]
40 | }
41 | const fn: CommandFn = originData => {
42 | const { group, other, raw_message, reg } = originData
43 | const num = reg.exec(raw_message)?.groups?.num
44 | const score = Number(num)
45 | if (score <= 0 || score > 10000000) return { items: 1, args: {} }
46 | setPacksack(group.id, other.id, { score })
47 | return { items: 2, args: { score } }
48 | }
49 | const addScore = { fn, sendContent }
50 |
51 | export { addScore }
52 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/draw/draw.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [抽奖]指令:
3 | * 用于在积分抽奖池中进行一次抽奖
4 | */
5 | import { judge, luckly } from './tools'
6 | import { setPacksack } from '../packsack/tools'
7 | import { formatNumCount, isDecimals } from '../../../lib/methods'
8 |
9 | import type { SendContent, CommandFn } from '../../../lib/interface'
10 |
11 | // 每位用户只能在上一次抽奖完成后,再进行下一次抽奖
12 | const drawPlaceholder = new Map()
13 |
14 | const sendContent: SendContent = {
15 | name: '抽奖',
16 | reg: /^抽奖$/,
17 | role: 'member',
18 | deverDefined: ({ user: { name }, defined: { reason, explain, score }, operations: { at } }) => [
19 | [
20 | `${at('user')} ${reason}`
21 | ],
22 | [
23 | `${at('user')} 小秋恭喜您抽到了(${explain})\n[本次抽奖积分变化:${score}积分]`,
24 | `${at('user')} 小秋帮您抽奖完成啦,您抽到的是(${explain})\n[本次抽奖积分变化:${score}积分]`,
25 | `呀 ${name}人品大爆发,抽到了(${explain})\n[本次抽奖积分变化:${score}积分]`
26 | ]
27 | ]
28 | }
29 | const fn: CommandFn = async originData => {
30 | const { group, user, database } = originData
31 | const isOk = await judge(group.id, user.id, 1, database, drawPlaceholder)
32 | const { flag, reason } = isOk
33 | if (!flag) return { items: 1, args: { reason } }
34 | // 记录本次抽奖是否完成
35 | const drawFlag = `${group.id}${user.id}`
36 | const count = 1
37 | drawPlaceholder.set(drawFlag, count)
38 | const { all, advance } = await luckly(group.id)
39 | await setPacksack(group.id, user.id, all)
40 | // 得到一次抽奖所消耗的积分
41 | // 确认更改数据后,清除原先所记录的抽奖标识
42 | drawPlaceholder.delete(drawFlag)
43 | const r = all.score
44 | return {
45 | items: 2,
46 | args: {
47 | explain: advance.explain,
48 | score: formatNumCount(isDecimals(r) ? Number(r.toFixed(1)) : r)
49 | }
50 | }
51 | }
52 | const draw = { fn, sendContent }
53 |
54 | export { draw }
55 |
--------------------------------------------------------------------------------
/eventsHandle/xiaoqiu/menu.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [菜单]指令:
3 | * 查看小秋菜单
4 | */
5 | import { getCurDarkOrLights } from '../../lib/time'
6 | import { getModeImg } from './tools'
7 | import { self } from '../../lib/user'
8 |
9 | import type { SendContent, CommandFn } from '../../lib/interface'
10 |
11 | const sendContent: SendContent = {
12 | name: '菜单',
13 | reg: [
14 | /^小秋菜单$/,
15 | /^菜单$/
16 | ],
17 | role: 'member',
18 | deverDefined: ({ user: { name, sex }, operations: { at } }) => [
19 | [
20 | `小秋菜单如下`,
21 | `${at('user')} 小${sex ? '哥哥' : '姐姐'},这就是小秋的全部功能啦`,
22 | `${name},悄悄告诉你,这就是小秋的全部魔法哦`,
23 | `${at('user')} 还想让小秋学会什么?赶快通过[提建议]指令告诉我吧`,
24 | `${at('user')} 听说有的指令会触发彩蛋哦~`
25 | ]
26 | ]
27 | }
28 | // 菜单指令分为浅色和深色模式,每个模式下各包含5张图片,分别对应着菜单中的5个模块:
29 | // 群聊相关、群管理操作、积分玩法、其它玩法、小秋相关
30 | const classify = [
31 | ['qlxg', '群聊相关'],
32 | ['qglcz', '群管理操作'],
33 | ['jfwf', '积分玩法'],
34 | ['qtwf', '其它玩法'],
35 | ['xqxg', '小秋相关']
36 | ]
37 | const fn: CommandFn = async originData => {
38 | const { bot, group, segment } = originData
39 | // 获取当前是白天还是黑天
40 | const flag = getCurDarkOrLights()
41 | const arr = getModeImg(classify, 'menu', flag)
42 | // 拿到路径后,因菜单较长,所以使用转发聊天记录的形式发出
43 | const info = {
44 | user_id: self.uin,
45 | nickname: '小秋菜单现有五个分类哦~'
46 | }
47 | const result = arr.map(([url]) => ({
48 | ...info,
49 | message: segment.image(url)
50 | }))
51 | const menuIntroduce = {
52 | ...info,
53 | message: '截止当前,小秋菜单共包含4个分类,每个分类下皆拥有若干指令\n(若某个指令序号后面带有符号☆,则代表该指令只能由特定用户进行触发)'
54 | }
55 | result.push(menuIntroduce)
56 | const forward = await bot.makeForwardMsg([...result])
57 | bot.sendGroupMsg(group.id, segment.xml(forward.data.data.data))
58 | return { items: 1 }
59 | }
60 | const menu = { fn, sendContent }
61 |
62 | export { menu }
63 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/notAllowedSpeak/relieveNotAllowedSpeak.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [解除禁止发言]指令:
3 | * 为某位成员解除禁止发言
4 | */
5 | import { persons, judgeMemberNotSpeak } from './tools'
6 |
7 | import type { SendContent, CommandFn } from '../../../lib/interface'
8 |
9 | const sendContent: SendContent = {
10 | name: '解除禁止发言',
11 | reg: /^解除禁止发言\[CQ:at,qq=\d*,text=.*\]\s*$/,
12 | role: 'admin',
13 | member: ({ other: { name }, operations: { at, face } }) => [
14 | `${at('user')} 听我说谢谢你 但是你还是不能使用该指令`,
15 | `${at('user')} 想啥呢 小秋没法帮您解除哦${face(34)}`,
16 | `${at('user')} ${name}不需要解除哦~`
17 | ],
18 | deverDefined: ({ user: { name: username }, other: { name: othername }, operations: { at } }) => [
19 | [
20 | `${at('user')} 不带这么玩的 ${othername}尚不处于禁止发言期间`,
21 | `${at('user')} 别闹,小秋没发现${othername}处于禁止发言期间`,
22 | `${at('user')} 等${othername}进入了禁止发言期间再来使用该指令吧!`
23 | ],
24 | [
25 | `${at('other')} 呜呼啦呼 你已被解除禁止发言`,
26 | `${at('other')} 人品大爆发 您被${username}解除了禁止发言`,
27 | `${at('other')} ${username}已解除您的禁止发言`
28 | ]
29 | ],
30 | equal: ({ other: { name }, operations: { at, face } }) => [
31 | `${at('user')} 本是同根生 相煎何太急`,
32 | `${at('user')} 小心${name}干你嗷${face(178)}`,
33 | `${at('user')} 都是同行 别这样`
34 | ],
35 | level: ({ operations: { at } }) => [
36 | `${at('user')} 抱歉 小秋暂时无法帮您完成此操作`,
37 | `${at('user')} 哎呀 小秋还没有[解除禁止发言]指令的权限呢`
38 | ]
39 | }
40 | const fn: CommandFn = originData => {
41 | const { group, other } = originData
42 | const isHave = judgeMemberNotSpeak(group.id, other.id)
43 | // 如果已经处于禁止发言期间了
44 | if (!isHave) return { items: 1 }
45 | const id = String(group.id) + String(other.id)
46 | persons.delete(id)
47 | return { items: 2 }
48 | }
49 | const relieveNotAllowedSpeak = { fn, sendContent }
50 |
51 | export { relieveNotAllowedSpeak }
52 |
--------------------------------------------------------------------------------
/lib/methods/tools.ts:
--------------------------------------------------------------------------------
1 | // 记录上次的随机数,避免重复
2 | const recordRandom = {
3 | randomScope: 0,
4 | random: 0
5 | }
6 | // 返回 0-x 之间的随机数
7 | const getRandomScope = (len: number): number => {
8 | if (len === 1) return 0
9 | const num = Math.floor(Math.random() * len)
10 | if (num === recordRandom.randomScope) return getRandomScope(len)
11 | recordRandom.randomScope = num
12 | return num
13 | }
14 | // 获取x-y之间的随机数,包含x和y
15 | const getRandom = (x: number, y: number): number => {
16 | const num = Math.round(Math.random() * (y - x) + x)
17 | if (num === recordRandom.random) return getRandom(x, y)
18 | recordRandom.random = num
19 | return num
20 | }
21 | // 打乱数组中的顺序(会影响原数组)
22 | const disruptOrder = (arr: []) => {
23 | const newArr: [] = []
24 | while (arr.length) {
25 | const [el] = arr.splice(getRandomScope(arr.length), 1)
26 | newArr.push(el)
27 | }
28 | return newArr
29 | }
30 | // 返回数组中的一个随机成员
31 | const returnOneOfContent = (contents: T[]): T => contents[getRandomScope(contents.length)]
32 | // 提取CQ码中的qq,并以字符串的形式返回
33 | const otherIdFromCq = (along: string): string | null => {
34 | const groups = /\[CQ:at,qq=(?\d*),text=.*\]/.exec(along)?.groups
35 | return groups?.qq!
36 | }
37 | // 获取一个由指定位数组成的随机数字字符串
38 | const getRandomDigitNumberStr = (digit: number) => {
39 | let str = ''
40 | for (let i = 1; i <= digit; i++) str += Math.floor(Math.random() * 10)
41 | return str
42 | }
43 | // 判断一个数有没有小数点
44 | const isDecimals = (num: number) => {
45 | const integer = Math.floor(num)
46 | return integer < num
47 | }
48 | // 判断一个数是正数还是负数
49 | // 例如3则返回'+3',-3则返回'-3'
50 | const formatNumCount = (num: number): string => (num >= 0 ? `+${num}` : `${num}`)
51 |
52 | export {
53 | getRandomScope,
54 | getRandom,
55 | returnOneOfContent,
56 | otherIdFromCq,
57 | disruptOrder,
58 | getRandomDigitNumberStr,
59 | isDecimals,
60 | formatNumCount
61 | }
62 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/notAllowedSpeak/notAllowedSpeak.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [禁止发言]指令:
3 | * 当某成员被禁止发言后,其所发出的每一条消息都会被小秋撤回(但并不会将该成员禁言)
4 | */
5 | import { persons, judgeMemberNotSpeak } from './tools'
6 |
7 | import type { SendContent, CommandFn } from '../../../lib/interface'
8 |
9 | const sendContent: SendContent = {
10 | name: '禁止发言',
11 | reg: /^禁止发言\[CQ:at,qq=\d*,text=.*\]\s*$/,
12 | role: 'admin',
13 | member: ({ other: { name }, operations: { at, face } }) => [
14 | `${at('user')} 要造反了吗???`,
15 | `${at('user')} 你居然要禁止${name}发言${face(34)}`,
16 | `${at('user')} 等你成为管理or群主就可以禁止别人发言了~`
17 | ],
18 | deverDefined: ({ user: { name: username }, other: { name: othername }, operations: { at, face } }) => [
19 | [
20 | `${at('user')} 不带这么玩的 ${othername}已经处于禁止发言期间了`,
21 | `${at('user')} 别闹 小秋看到${othername}已经被禁止发言了`,
22 | `${at('user')} 不可以对${othername}同时使用两次[禁止发言]指令哦`
23 | ],
24 | [
25 | `${at('other')} 您现在已进入禁止发言期间`,
26 | `${username}已将${othername}列入禁止发言名单${face(178)}`,
27 | `${at('other')} 你已进入禁止发言期间,在此期间 您发送的所有消息都会被一一撤回`
28 | ]
29 | ],
30 | equal: ({ other: { name }, operations: { at, face } }) => [
31 | `${at('user')} 本是同根生 相煎何太急`,
32 | `${at('user')} 小心${name}干你嗷${face(178)}`,
33 | `${at('user')} 都是同行 别这样`
34 | ],
35 | level: ({ operations: { at } }) => [
36 | `${at('user')} 抱歉 小秋暂时无法帮您完成此操作`,
37 | `${at('user')} 哎呀 小秋还没有[禁止发言]指令的权限呢`
38 | ]
39 | }
40 | const fn: CommandFn = originData => {
41 | const { group, other } = originData
42 | const isHave = judgeMemberNotSpeak(group.id, other.id)
43 | // 如果已经处于禁止发言期间了
44 | if (isHave) return { items: 1 }
45 | const id = String(group.id) + String(other.id)
46 | persons.set(id, true)
47 | return { items: 2 }
48 | }
49 | const notAllowedSpeak = { fn, sendContent }
50 |
51 | export { notAllowedSpeak }
52 |
--------------------------------------------------------------------------------
/eventsHandle/fns.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 注意,所有指令的正则之间可能会发生冲突,例如[禁言卡@成员]与[禁言1分钟]
3 | * 解决冲突的办法就是在export中尽可能让复杂的正则排在前面,或者修改正则触发方式
4 | */
5 | import { askQuestions, signIn } from './groupchat'
6 | import {
7 | runtime,
8 | getCopyWriting,
9 | goodNight,
10 | getSuggestions,
11 | landlords,
12 | goodMorning,
13 | listenMusic,
14 | makeSuggestion,
15 | xiaoqiuChat,
16 | searchJueJin
17 | } from './otherPlay'
18 | import {
19 | changeDrawDiscount,
20 | draw,
21 | drawCount,
22 | getDrawDiscount,
23 | getPacksack,
24 | addScore,
25 | banAndDelCard,
26 | seeCard,
27 | getScoreWay
28 | } from './scorePlayMethods'
29 | import {
30 | alert,
31 | banCommon,
32 | checkDeadPerson,
33 | relieveCheckDeadPerson,
34 | notAllowedSpeak,
35 | relieveNotAllowedSpeak,
36 | delMemberMessage,
37 | setCard,
38 | setGroupKick,
39 | setGroupWholeBan
40 | } from './groupchatAdmin'
41 | import { isXiaoQiuOnline, menu, switchCommand, versions } from './xiaoqiu'
42 |
43 | const commandsConfig = {
44 | askQuestions,
45 | signIn,
46 | runtime,
47 | getCopyWriting,
48 | goodNight,
49 | getSuggestions,
50 | landlords,
51 | goodMorning,
52 | listenMusic,
53 | makeSuggestion,
54 | xiaoqiuChat,
55 | searchJueJin,
56 | changeDrawDiscount,
57 | draw,
58 | drawCount,
59 | getDrawDiscount,
60 | getPacksack,
61 | addScore,
62 | banAndDelCard,
63 | seeCard,
64 | getScoreWay,
65 | alert,
66 | banCommon,
67 | checkDeadPerson,
68 | relieveCheckDeadPerson,
69 | notAllowedSpeak,
70 | relieveNotAllowedSpeak,
71 | delMemberMessage,
72 | setCard,
73 | setGroupKick,
74 | setGroupWholeBan,
75 | isXiaoQiuOnline,
76 | menu,
77 | switchCommand,
78 | versions
79 | } as const
80 |
81 | type CommandsConfig = typeof commandsConfig
82 |
83 | export {
84 | commandsConfig,
85 | CommandsConfig
86 | }
87 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/alert.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [警告]指令:
3 | * 撤回指定成员的全部消息,并禁言15分钟
4 | */
5 | import path from 'path'
6 |
7 | import type { SendContent, CommandFn } from '../../lib/interface'
8 |
9 | const sendContent: SendContent = {
10 | name: '警告',
11 | reg: /^警告\[CQ:at,qq=(?\d*),text=.*\]\s*$/,
12 | role: 'admin',
13 | member: ({ user: { name }, other: { role }, operations: { at, promiseImage } }) => [
14 | `${at('user')} 等你成为管理就可以警告别人了`,
15 | `${at('user')} 这指令只有管理or群主才可以触发哦~`,
16 | `${at('user')} 想弄${role === 'admin' ? '管理' : '群主'}搞事情?`,
17 | `${at('other')} ${name}想要警告你${promiseImage(path.resolve('./assets/images/emoji/麻了1.jpg'))}`
18 | ],
19 | admin: ({ user: { name }, operations: { at } }) => [
20 | `${at('other')} 小秋发现管理员${name}对您发出了[警告]指令,因此小秋已撤回您1小时内所有消息,并禁言以作警告`,
21 | `${at('other')} 呜呜呜 小秋被臭管理逮到了,${name}对您发出了[警告]指令,因此小秋已撤回您1小时内所有消息,并禁言以作警告`,
22 | `${at('other')} 请务必文明发言。[本次已撤回您1小时内所有消息,并禁言以作警告]`
23 | ],
24 | owner: ({ user: { name }, operations: { at } }) => [
25 | `${at('other')} 小秋发现群主大人${name}对您发出了[警告]指令,因此小秋已撤回您1小时内所有消息,并禁言以作警告`,
26 | `${at('other')} 呜呜呜 小秋被臭群主逮到了,${name}对您发出了[警告]指令,因此小秋已撤回您1小时内所有消息,并禁言以作警告`,
27 | `${at('other')} 请务必文明发言。[本次已撤回您1小时内所有消息,并禁言以作警告]`
28 | ],
29 | equal: ({ user: { name: username }, other: { name: othername }, operations: { at, face } }) => [
30 | `${at('user')} 嗯??大家都是同道中人,干嘛要禁言${othername}呢`,
31 | `${at('user')} 糟了 小秋无法帮您进行警告`,
32 | `${at('other')} ${username}要警告你!${face(278)}`
33 | ],
34 | level: ({ operations: { at } }) => [
35 | `${at('user')} 抱歉 小秋权限不足 暂时不能帮您执行警告操作哦~`,
36 | `${at('user')} 小秋暂无此权限`
37 | ]
38 | }
39 | const fn: CommandFn = originData => {
40 | const { group, other, operations } = originData
41 | operations.delMsg(group.id, other.id, 0)
42 | operations.banMember(group.id, other.id, '分钟', 15)
43 | }
44 | const alert = { fn, sendContent }
45 |
46 | export { alert }
47 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/packsack/getPacksack.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [我的背包]指令:
3 | * 发送当前用户背包中的积分、道具卡数量
4 | */
5 | import { isDecimals } from '../../../lib/methods'
6 |
7 | import type { SendContent, CommandFn } from '../../../lib/interface'
8 |
9 | const sendContent: SendContent = {
10 | name: '我的背包',
11 | reg: [
12 | /^我的背包$/,
13 | /^我的积分$/,
14 | /^查询背包$/,
15 | /^背包查询$/,
16 | /^查询积分$/,
17 | /^积分查询$/,
18 | /^查看背包$/,
19 | /^查看积分$/
20 | ],
21 | role: 'member',
22 | deverDefined: ({ defined: { score, ban, delMsg, see, immune }, operations: { at } }) => [
23 | [
24 | `${at('user')} 小秋查询到您的背包如下~\n积分:${score}分\n禁言卡:${ban}张,免疫卡:${immune}张\n撤回卡:${delMsg}张,康康卡:${see}张`,
25 | `${at('user')}\n积分${score}\n禁言卡${ban}张\n免疫卡${immune}张\n撤回卡${delMsg}张\n康康卡${see}张`,
26 | `${at('user')} 您的背包情况为:${score}积分,${ban}张禁言卡、${immune}张免疫卡,${delMsg}张撤回卡、康康卡${see}张!`
27 | ],
28 | [
29 | `${at('user')} 小秋查询到您的背包如下哦~\n积分:${score}分\n禁言卡:${ban}张\n免疫卡:${immune}张\n撤回卡:${delMsg}张\n康康卡:${see}张`,
30 | `${at('user')} 哇 太多了叭 羡慕啊\n积分:${score}分\n禁言卡:${ban}张\n免疫卡:${immune}张\n撤回卡:${delMsg}张\n康康卡:${see}张`
31 | ]
32 | ]
33 | }
34 | const fn: CommandFn = async originData => {
35 | const { group, user: { id }, database } = originData
36 | const { getDataBaseData, formSet: { user_packsack } } = database
37 | const value = await getDataBaseData(user_packsack.name, user_packsack.retrieveData)(group.id)
38 | const all = value[id] || { score: 0, card: { ban: 0, immune: 0, delMsg: 0, see: 0 } }
39 | const { score: myScore, card } = all
40 | const { ban, immune, delMsg, see } = card
41 | const score = isDecimals(myScore) ? myScore.toFixed(1) : myScore
42 | if ([ban, immune, delMsg, see].find(v => v >= 100))
43 | return { items: 2, args: { score, ban, immune, delMsg, see } }
44 | return { items: 1, args: { score, ban, immune, delMsg, see } }
45 | }
46 | const getPacksack = { fn, sendContent }
47 |
48 | export { getPacksack }
49 |
--------------------------------------------------------------------------------
/eventsHandle/proxy/verifyAnswer.ts:
--------------------------------------------------------------------------------
1 | import { persons } from '../groupchatAdmin/checkDeadPerson/tools'
2 |
3 | import type { VerifyProxy } from './interface'
4 |
5 | // 存储正在进行人机验证的用户
6 | const answerTip = '答案中不应该带有修饰性词语,否则判断为错误'
7 | // 检测当前消息是否为人机验证的作答消息
8 | const verifyAnswer: VerifyProxy = info => {
9 | const { bot, gId, uId, operations, raw_message } = info
10 | // 如果没有添加人机验证,则直接返回false
11 | if (!persons) return false
12 | const id = String(gId) + String(uId)
13 | // 当前用户是否为正在进行人机验证的成员
14 | const member = persons.get(id)
15 | if (!member) return false
16 | const { answer, times, isFailed, token } = member
17 | const { doAt: at, delMsg } = operations
18 | // 如果当前用户3分钟内未作答,或3分钟内的3次作答机会全部错误,此时会在15秒后踢出本成员
19 | // 但这15秒中,用户还有可能发送消息,所以只要检测到这位成员发送的消息,就立即撤回
20 | if (isFailed) {
21 | delMsg(gId, uId, 1)
22 | bot.sendGroupMsg(gId, `${at(uId)},请耐心等待15秒计时结束~`)
23 | return true
24 | }
25 | // 如果属于正在进行人机验证的成员,则对该条消息进行验证
26 | const isTrue = answer.find(r => r === raw_message.trim())
27 | // 如果作答成功
28 | if (isTrue) {
29 | // 关闭之前开启的定时器
30 | clearTimeout(token)
31 | // 删除id
32 | persons.delete(id)
33 | bot.sendGroupMsg(gId, `${at(uId)},小秋检测到您已成功完成人机验证,恭喜~`)
34 | return true
35 | }
36 | const nextTimes = times + 1
37 | persons.set(id, { ...member, times: nextTimes })
38 | // 如果次数已用尽
39 | if (nextTimes >= 3) {
40 | persons.set(id, { ...member, times: nextTimes, isFailed: true })
41 | setTimeout(() => {
42 | const have = persons.get(id)
43 | if (!have) return
44 | // 关闭定时器
45 | clearTimeout(have.token)
46 | // 清除该id
47 | persons.delete(id)
48 | bot.setGroupKick(gId, uId)
49 | }, 1000 * 15)
50 | bot.sendGroupMsg(gId, `${at(uId)},检测到您所提交的三个答案全部错误,因此15秒后您将被踢出此群聊\n\n[如误判,请添加QQ二群191515766进行说明]`)
51 | return true
52 | }
53 | bot.sendGroupMsg(gId, `${at(uId)},本次回答错误,您还有${3 - nextTimes}次机会\n[温馨提示:${answerTip}]`)
54 | return true
55 | }
56 |
57 | export { verifyAnswer }
58 |
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/getCopyWriting.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [文案]指令:
3 | * 使用一言Api随机获取指定类型的文案
4 | */
5 | import request from 'request'
6 |
7 | import type { YiYanApi } from './interface'
8 | import type { SendContent, CommandFn } from '../../lib/interface'
9 |
10 | type WritingType = keyof typeof reqContent
11 |
12 | const reqContent = {
13 | 动画: 'a',
14 | 漫画: 'b',
15 | 游戏: 'c',
16 | 文学: 'd',
17 | 原创: 'e',
18 | 网络: 'f',
19 | 其它: 'g',
20 | 影视: 'h',
21 | 诗词: 'i',
22 | 哲学: 'k'
23 | }
24 |
25 | const sendContent: SendContent = {
26 | name: '文案',
27 | reg: /^文案:(?.*)\s*$/,
28 | role: 'member',
29 | deverDefined: ({ user: { name }, defined: { message }, operations: { at } }) => [
30 | [
31 | `${at('user')} 小秋发现您还没有输入文案类型`,
32 | `${name},必须输入一个文案类型才可以哦~`,
33 | `${at('user')} 哎呀 没有文案类型的话,小秋无法帮您寻找哦~`
34 | ],
35 | [
36 | `${at('user')} 小秋发现您的文案类型有误,您可以尝试更换类型后重新进行搜索`,
37 | `${name},哎呀 该类型走丢了,请换个类型重新尝试吧~`,
38 | `${at('user')} 暂时没有找到该文案类型哦`
39 | ],
40 | [`${name},${message}`, `${message}`]
41 | ]
42 | }
43 |
44 | const fn: CommandFn = originData => {
45 | const { raw_message, reg } = originData
46 | return new Promise(resolve => {
47 | const type = reg.exec(raw_message)?.groups?.c as WritingType
48 | if (!type) return resolve({ items: 1, args: {} })
49 | const flag = Object.keys(reqContent).includes(type)
50 | if (!flag) return resolve({ items: 2, args: {} })
51 | const c = reqContent[type]
52 | request(`https://v1.hitokoto.cn`, { qs: { c, encode: 'json' } }, (err: unknown, rep: unknown, data: string) => {
53 | if (!!data) {
54 | const { from, from_who, hitokoto }: YiYanApi = JSON.parse(data)
55 | const writer = from_who ? from_who : ''
56 | const message = `${hitokoto}\n——${writer}《${from}》`
57 | resolve({ items: 3, args: { message } })
58 | }
59 | }
60 | )
61 | })
62 | }
63 | const getCopyWriting = { fn, sendContent }
64 |
65 | export { getCopyWriting }
66 |
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/getSuggestions.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [查建议]指令:
3 | * 获取所有用户给小秋提出的建议,每条建议均配备“进度”标识,代表当前所提出的建议进度
4 | */
5 | import { getWholeDate } from '../../lib/time'
6 | import { self } from '../../lib/user'
7 |
8 | import type { SuggestionFormat } from '../../database/forms/interface'
9 | import type { SendContent, CommandFn, ForwardRecord } from '../../lib/interface'
10 |
11 | const sendContent: SendContent = {
12 | name: '查建议',
13 | reg: [
14 | /^查建议$/,
15 | /^查看建议$/,
16 | /^看建议$/,
17 | /^查询建议$/
18 | ],
19 | role: 'member',
20 | deverDefined: ({ defined: { tip } }) => [
21 | [
22 | `小秋暂时没有发现更多建议`, `暂时没有更多建议,快来提出第一条建议吧`
23 | ],
24 | [
25 | `${tip}`
26 | ]
27 | ]
28 | }
29 | // 处理建议
30 | const centerLine = (i: number) => (i > 1 ? '\n- - -\n' : '')
31 | const process = (json: string) => {
32 | const arrs: SuggestionFormat[] = JSON.parse(json)
33 | let text = ''
34 | const handleResult = (result: string | undefined) => result ? `\n处理结果:${result}` : ''
35 | arrs.forEach(sug => {
36 | const { content, timestamp, plan, result } = sug
37 | text += `建议:『${content}』\n提出时间:${getWholeDate(new Date(timestamp))}\n当前进度:${plan}${handleResult(result)}${centerLine(arrs.length)}`
38 | })
39 | return text
40 | }
41 | const fn: CommandFn = async originData => {
42 | const { bot, segment, group, database } = originData
43 | const { getDataBaseData, formSet: { suggestions } } = database
44 | const data = await getDataBaseData(suggestions.name, suggestions.retrieveData)()
45 | if (!data) return { items: 1 }
46 | const result: ForwardRecord[] = []
47 | data.forEach(row => result.push({
48 | user_id: self.uin,
49 | nickname: '来自小秋用户的建议',
50 | message: process(row.suggestion)
51 | }))
52 | const forward = await bot.makeForwardMsg([...result])
53 | bot.sendGroupMsg(group.id, segment.xml(forward.data.data.data))
54 | const tip = '只要是小秋的用户,便可看到所有关于小秋的建议,以此才能更好地帮助小秋成长'
55 | return { items: 2, args: { tip } }
56 | }
57 | const getSuggestions = { fn, sendContent }
58 |
59 | export { getSuggestions }
60 |
--------------------------------------------------------------------------------
/assets/xiaoqiu_alpha.sql:
--------------------------------------------------------------------------------
1 | /*
2 | Navicat Premium Data Transfer
3 |
4 | Source Server : database
5 | Source Server Type : MySQL
6 | Source Server Version : 80026
7 | Source Host : localhost:3306
8 | Source Schema : xiaoqiu_alpha
9 |
10 | Target Server Type : MySQL
11 | Target Server Version : 80026
12 | File Encoding : 65001
13 |
14 | Date: 24/10/2022 00:00:00
15 | */
16 |
17 | SET NAMES utf8mb4;
18 | SET FOREIGN_KEY_CHECKS = 0;
19 |
20 | -- ----------------------------
21 | -- Table structure for groups_config
22 | -- ----------------------------
23 | DROP TABLE IF EXISTS `groups_config`;
24 | CREATE TABLE `groups_config` (
25 | `group_id` int(0) NOT NULL,
26 | `config` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
27 | PRIMARY KEY (`group_id`) USING BTREE
28 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
29 |
30 | -- ----------------------------
31 | -- Table structure for suggestions
32 | -- ----------------------------
33 | DROP TABLE IF EXISTS `suggestions`;
34 | CREATE TABLE `suggestions` (
35 | `user_id` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
36 | `suggestion` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL
37 | ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
38 |
39 | -- ----------------------------
40 | -- Table structure for switch_commands
41 | -- ----------------------------
42 | DROP TABLE IF EXISTS `switch_commands`;
43 | CREATE TABLE `switch_commands` (
44 | `group_id` int(0) NOT NULL DEFAULT 0,
45 | `commands` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
46 | PRIMARY KEY (`group_id`) USING BTREE
47 | ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
48 |
49 | -- ----------------------------
50 | -- Table structure for user_packsack
51 | -- ----------------------------
52 | DROP TABLE IF EXISTS `user_packsack`;
53 | CREATE TABLE `user_packsack` (
54 | `group_id` int(0) NOT NULL,
55 | `whole_user_id` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
56 | PRIMARY KEY (`group_id`) USING BTREE
57 | ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
58 |
59 | SET FOREIGN_KEY_CHECKS = 1;
60 |
--------------------------------------------------------------------------------
/eventsHandle/groupchat/signIn.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [签到]指令:
3 | * 签到被触发时,会根据当前群成员的签到天数自动发放积分
4 | * (每日仅可签到一次,凌晨12:00重置次数)
5 | */
6 | import path from 'path'
7 |
8 | import { setPacksack } from '../scorePlayMethods/packsack/tools'
9 |
10 | import type { SendContent, CommandFn } from '../../lib/interface'
11 |
12 | const sendContent: SendContent = {
13 | name: '签到',
14 | reg: [
15 | /^签到$/,
16 | /^冒泡$/,
17 | /^打卡$/,
18 | /^活跃$/,
19 | /^摸鱼$/,
20 | /^疯狂星期四$/,
21 | /^肯德基疯狂星期四$/,
22 | /^vme50$/,
23 | /^v我50$/,
24 | /^v50$/
25 | ],
26 | role: 'member',
27 | deverDefined: ({ user: { name }, defined: { score }, operations: { at, face, promiseImage } }) => [
28 | [
29 | `${at('user')} 您今日已经签过到了哦~`,
30 | `${at('user')} 不允许重复签到,请明日再来吧`,
31 | `${at('user')} 今日已签到过了~\n[每日凌晨12点重置签到次数]`
32 | ],
33 | [
34 | `${at('user')} 努力做一位前端追梦人吧!\n${score}`,
35 | `${at('user')} 加油 前端人!${score}${face(183)}`,
36 | `${name},不积跬步无以至千里 今天也要多多敲代码哦~\n${score}`,
37 | `${at('user')} 听说vue3与vite搭配 风味更佳哦~\n${score}`,
38 | `${name},我们都在努力奔跑,我们都是追梦人\n${score}`,
39 | `${at('user')} 欲速则不达 今天你准备怎么提升自己呢?${score}${promiseImage(
40 | path.resolve('./assets/images/emoji/加倍焦虑1.jpg')
41 | )}`
42 | ]
43 | ]
44 | }
45 | const fn: CommandFn = async originData => {
46 | const { group, user, database } = originData
47 | const { getDataBaseData, formSet: { user_packsack, groups_config } } = database
48 | const value = await getDataBaseData(user_packsack.name, user_packsack.retrieveData)(group.id)
49 | const curUser = value[user.id]
50 | // 获取当前群聊的每日积分上限
51 | const groupConfig = await getDataBaseData(groups_config.name, groups_config.retrieveData)(group.id)
52 | const { score: { dailyLimit } } = groupConfig
53 | const isMaxCurScore = curUser ? curUser.curInc >= dailyLimit : false
54 | const update = () => {
55 | setPacksack(group.id, user.id, { score: dailyLimit, curInc: dailyLimit })
56 | return `积分+${dailyLimit}`
57 | }
58 | if (isMaxCurScore) return { items: 1 }
59 | const score = `[${update()}]`
60 | return { items: 2, args: { score } }
61 | }
62 |
63 | const signIn = { fn, sendContent }
64 |
65 | export { signIn }
66 |
--------------------------------------------------------------------------------
/lib/oicq/oicqOperations.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | import { createClient, segment, cqcode } from 'oicq'
3 |
4 | import { getDataBaseData } from '../../database/tools'
5 | import { belong } from '../time'
6 | import { self } from '../user'
7 | import { cacheGroupMsg } from './tools'
8 |
9 | import type { OicqOperations } from './interface'
10 | import type { UnitChi } from '../time/interface'
11 |
12 | const { uin, pwd } = self
13 |
14 | // 创建客户端(传递机器人(小秋)的账号)
15 | const bot = createClient(uin)
16 | // 登录
17 | bot.login(pwd)
18 | // 艾特
19 | const doAt: OicqOperations['doAt'] = id => segment.toCqcode(segment.at(id))
20 | // 发送表情
21 | const face: OicqOperations['face'] = order => segment.toCqcode(segment.face(order))
22 | // 查询某个群聊的历史消息,如果未指定查询的条数,则默认为当前群聊中的全部历史消息
23 | // 从后面往前查historyMsg: [1, 2, 3, 4, 5, 6],如果是3,则返回4 5 6 条消息
24 | const getHistoryGroupMsg: OicqOperations['getHistoryGroupMsg'] = (gId, counts?) => {
25 | const arrs = cacheGroupMsg[gId]
26 | if (!counts) return arrs
27 | // 返回指定的条数
28 | const len = counts >= arrs.length ? 0 : arrs.length - counts
29 | const msg = arrs.slice(len)
30 | return msg
31 | }
32 | // 撤回
33 | const delMsg: OicqOperations['delMsg'] = async (groupId, otherId, num) => {
34 | // num为0则代表删除全部消息(全部消息 === 最近30条消息)
35 | const preMessage = cacheGroupMsg[groupId]
36 | const row = num === 0 ? 30 : num
37 | let i = preMessage.length - 1
38 | let beCountMsg = 0
39 | while (i >= 0) {
40 | if (beCountMsg === row) break
41 | if (preMessage[i].userId === otherId) {
42 | const [single] = preMessage.splice(i, 1)
43 | bot.deleteMsg(single.msgId)
44 | beCountMsg++
45 | }
46 | i--
47 | }
48 | // 返回已撤回的消息条数
49 | return beCountMsg
50 | }
51 | // 禁言,传入单位(分钟、小时、天数) 以及 持续时间(1、2、...)
52 | const banMember: OicqOperations['banMember'] = (groupId, otherId, unit, dur) =>
53 | bot.setGroupBan(groupId, otherId, belong[unit as UnitChi].sec(dur))
54 |
55 | // 获取指定文件中的图片,以备发送(返回图片的CQ码,可以单独发送,也可以内嵌在字符串中发送)
56 | // 在聊天记录中发送图片,需要使用segment.image,而不是cqcode.image
57 | const promiseImage: OicqOperations['promiseImage'] = imgPath => cqcode.image(imgPath)
58 |
59 | const operations = {
60 | getDataBaseData,
61 | doAt,
62 | face,
63 | getHistoryGroupMsg,
64 | delMsg,
65 | banMember,
66 | promiseImage
67 | }
68 |
69 | export {
70 | bot,
71 | segment,
72 | operations
73 | }
74 |
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/xiaoqiuChat.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [:]指令:
3 | * 使用在线聊天Api,与另一位机器人对话,其表现形式类似于语音助手。使用该指令会自动消耗当前用户1积分
4 | */
5 | import request from 'request'
6 |
7 | import { setPacksack } from '../scorePlayMethods/packsack/tools'
8 |
9 | import type { GroupAt } from '../../lib/interface'
10 | import type { SendContent, CommandFn } from '../../lib/interface'
11 |
12 | const successTip = '\n[您消耗了1积分]'
13 | const msg = (at: GroupAt, names: string) => [
14 | `${at('user')} 您的积分不足以完成此次对话`,
15 | `${names},您的积分太少啦,不能够继续发起本次会话哦~`
16 | ]
17 | const sendContent: SendContent = {
18 | name: '在线聊天',
19 | reg: /^:/,
20 | role: 'member',
21 | member: ({ user: { name }, operations: { at } }) => msg(at, name),
22 | admin: ({ user: { name }, operations: { at } }) => msg(at, name),
23 | owner: ({ user: { name }, operations: { at } }) => msg(at, name),
24 | deverDefined: ({ user: { name }, operations: { at }, defined }) => [
25 | [
26 | `${at('user')} 呜呜 必须输入有效内容才可以进行对话哦~\n[此次未消耗积分]`,
27 | `${at('user')} 咦?暂时没有发现您所输入的聊天内容哦~\n[此次未消耗积分]`,
28 | `${name} 主动一点呀,您可以输入更多的聊天内容来重新尝试发起此会话~\n[此次未消耗积分]`
29 | ],
30 | [
31 | `${at('user')} ${defined.content}${successTip}`,
32 | `${name},${defined.content}${successTip}`
33 | ]
34 | ]
35 | }
36 | const fn: CommandFn = originData =>
37 | new Promise(async res => {
38 | const { group, user, raw_message, database } = originData
39 | const { getDataBaseData, formSet: { user_packsack } } = database
40 | const value = await getDataBaseData(user_packsack.name, user_packsack.retrieveData)(group.id)
41 | const curUser = value[user.id]
42 | if (!curUser) return res(undefined)
43 | const { score } = curUser
44 | if (Number(score) < 1) return res(undefined)
45 | const content = raw_message.replace(/:|\s/g, '')
46 | if (!content) return res({ items: 1, args: {} })
47 | const url = 'https://api.ownthink.com/bot?appid=xiaosi&userid=user&spoken=' + encodeURIComponent(content)
48 | request(url, (_: unknown, __: unknown, data: string) => {
49 | setPacksack(group.id, user.id, { score: -1 })
50 | const content = JSON.parse(data).data.info.text
51 | res({ items: 2, args: { content } })
52 | })
53 | })
54 | const xiaoqiuChat = { fn, sendContent }
55 |
56 | export { xiaoqiuChat }
57 |
--------------------------------------------------------------------------------
/lib/oicq/interface.ts:
--------------------------------------------------------------------------------
1 | import type { DataBase } from '../../database/interface'
2 | import type { PreGroupchatMessage } from '../../database/forms/interface'
3 | import type { OriginData, MemberBasisInfo, OriginMemberBasisInfo, GroupchatsId } from '../interface'
4 |
5 | type DoAt = (id: number) => string
6 | type ToCqCode = (id: number) => string
7 | type GetHistoryGroupMsg = (gId: GroupchatsId, counts?: number) => PreGroupchatMessage[]
8 | type DelMsg = (groupId: GroupchatsId, otherId: number, num: number) => Promise
9 | type BanMember = (groupId: GroupchatsId, otherId: number, unit: string, dur: number) => string
10 | type PromiseImage = (imgPath: string) => string
11 |
12 | type OicqOperations = {
13 | doAt: DoAt
14 | face: ToCqCode
15 | getHistoryGroupMsg: GetHistoryGroupMsg
16 | delMsg: DelMsg
17 | banMember: BanMember
18 | promiseImage: PromiseImage
19 | }
20 | // 事件处理函数
21 | type GroupData = {
22 | group_id: GroupchatsId
23 | sender: MemberBasisInfo
24 | message_id: string
25 | raw_message: string
26 | group_name: string
27 | self_id: number
28 | atme: boolean
29 | }
30 | type OriginGroupData = {
31 | group_id: GroupchatsId
32 | sender: OriginMemberBasisInfo
33 | message_id: string
34 | raw_message: string
35 | group_name: string
36 | self_id: number
37 | atme: boolean
38 | }
39 | type IncreaseData = {
40 | group_id: GroupchatsId
41 | user_id: number
42 | nickname: string
43 | }
44 | type BanData = {
45 | group_id: number
46 | user_id: number
47 | operator_id: number
48 | duration: number
49 | }
50 | type DecreaseData = {
51 | group_id: number
52 | operator_id: number
53 | member: {
54 | user_id: number
55 | card: string
56 | role: string
57 | nickname: string
58 | }
59 | }
60 | type NoticeGroupPoke = {
61 | group_id: number
62 | self_id: number
63 | user_id: number
64 | target_id: number
65 | operator_id: number
66 | action: string
67 | suffix: string
68 | }
69 | // 用于存储群聊信息/数据,然后传递给被正则触发的函数
70 | type HandleMessage = {
71 | data: OriginGroupData
72 | bot: OriginData['bot']
73 | operations: OriginData['operations']
74 | segment: OriginData['segment']
75 | database: DataBase
76 | }
77 |
78 | export {
79 | OicqOperations,
80 | GroupData,
81 | OriginGroupData,
82 | IncreaseData,
83 | BanData,
84 | DecreaseData,
85 | NoticeGroupPoke,
86 | HandleMessage
87 | }
88 |
--------------------------------------------------------------------------------
/eventsHandle/arguments/info.ts:
--------------------------------------------------------------------------------
1 | import { nicknameFormCard } from '../../lib/groupchat'
2 | import { getDataBaseData, formSet } from '../../database'
3 |
4 | import type { HandleMessage } from '../../lib/oicq/interface'
5 | import type { GroupAt, GroupBasisInfo, InfoTemp, MemberBasisInfo } from '../../lib/interface'
6 |
7 | // 格式化一些指令所需信息
8 | const getUserInfo = (info: HandleMessage) => {
9 | const { sender } = info.data
10 | const user: MemberBasisInfo = {
11 | id: sender.user_id,
12 | sex: sender.sex != 'female',
13 | role: sender.role,
14 | card: sender.card,
15 | nickname: sender.nickname,
16 | name: nicknameFormCard({
17 | nickname: sender.nickname,
18 | card: sender.card
19 | })
20 | }
21 | return user
22 | }
23 | const member = 'member' as const
24 | const getOtherInfo = (
25 | id = 10000,
26 | sex = true,
27 | card = '',
28 | role = member,
29 | nickname = '',
30 | name = ''
31 | ) => {
32 | const other: MemberBasisInfo = { id, sex, card, role, nickname, name }
33 | return other
34 | }
35 | const getGroupInfo = (info: HandleMessage) => {
36 | const { group_id, group_name } = info.data
37 | const group: GroupBasisInfo = {
38 | id: group_id,
39 | name: group_name
40 | }
41 | return group
42 | }
43 | const getWantOriginData = (
44 | info: HandleMessage,
45 | user: MemberBasisInfo,
46 | other: MemberBasisInfo,
47 | group: GroupBasisInfo,
48 | reg: RegExp
49 | ) => {
50 | const { bot, data, operations, segment } = info
51 | const { raw_message } = data
52 | const wantOriginData = {
53 | bot,
54 | segment,
55 | user,
56 | other,
57 | group,
58 | raw_message,
59 | reg,
60 | operations,
61 | database: {
62 | getDataBaseData,
63 | formSet
64 | }
65 | }
66 | return wantOriginData
67 | }
68 | const getInfoTemp = (info: HandleMessage, user: MemberBasisInfo, at: GroupAt) => {
69 | const { operations } = info
70 | const infoTemp: InfoTemp = {
71 | user,
72 | other: {
73 | id: 10000,
74 | card: '',
75 | role: 'member',
76 | sex: true,
77 | nickname: '',
78 | name: ''
79 | },
80 | defined: {},
81 | operations: {
82 | at: at,
83 | face: operations.face,
84 | promiseImage: operations.promiseImage
85 | }
86 | }
87 | return infoTemp
88 | }
89 |
90 | export {
91 | getUserInfo,
92 | getOtherInfo,
93 | getGroupInfo,
94 | getWantOriginData,
95 | getInfoTemp
96 | }
97 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/checkDeadPerson/relieveCheckDeadPerson.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [解除人机验证]指令:
3 | * 用于为某位成员手动解除人机验证,解除后,当前成员不受“人机验证”的任何限制
4 | */
5 | import path from 'path'
6 |
7 | import { persons, inCheckDeadOf } from './tools'
8 |
9 | import type { SendContent, CommandFn } from '../../../lib/interface'
10 |
11 | const sendContent: SendContent = {
12 | name: '解除人机验证',
13 | reg: /^\s*解除人机验证\[CQ:at,qq=(?\d*),text=.*\]\s*$/,
14 | role: 'admin',
15 | member: ({ user: { sex, name }, operations: { at, face, promiseImage } }) => [
16 | `${at('user')} 您的权限不足以进行解除人机验证的操作${promiseImage(path.resolve('./assets/images/emoji/反了你了1.jpg'))}`,
17 | `${name},抱歉 小秋检测到您暂未拥有[解除人机验证]指令的权限`,
18 | `${at('other')}${sex ? '哥哥' : '姐姐'},<${at('user')}>要对你进行人机验证${face(178)}`,
19 | `${at('user')} 要造反了???这[解除人机验证]可不能说用就用啊`,
20 | `${name},迎难而上??小心被反验证哦~`,
21 | `${at('user')} 抱歉 小秋不能帮您执行该指令`
22 | ],
23 | deverDefined: ({ user: { name: username, sex }, other: { name: othername }, operations: { at, face, promiseImage } }) => [
24 | [
25 | `${at('user')} 哎呀 ${othername}当前未在进行人机验证哦,因此无需解除`,
26 | `${at('user')} 小秋检测到${othername}未处于人机验证环节,可以不用解除哦${face(176)}`,
27 | `${at('user')} 不可以对未进行人机验证的成员进行解除哦~`,
28 | `${at('other')} 快谢谢${username},虽然你没有在进行人机验证,但${sex ? '他' : '她'}还想帮你解除哦~${promiseImage(path.resolve('./assets/images/emoji/比心3.jpg'))}`
29 | ],
30 | [
31 | `${at('other')} ${username}已关闭您目前所处于的人机验证阶段${face(180)}`,
32 | `${at('other')} 您当前所进行的人机验证已关闭\n[来自${username}]`,
33 | `${at('other')} 人品大爆发 ${username}帮您解除了人机验证${promiseImage(path.resolve('./assets/images/emoji/朋友醒醒1.jpg'))}`
34 | ]
35 | ],
36 | equal: ({ other: { name }, operations: { at, face } }) => [
37 | `${at('user')} 大家都一样 不用给${name}解除${face(178)}`,
38 | `${at('user')} 哎呀 ${name}不需要小秋进行解除人机验证哦`,
39 | `${at('user')} 放心好啦 ${name}能够免疫人机验证`
40 | ],
41 | level: ({ operations: { at, promiseImage } }) => [
42 | `${at('user')} 抱歉 小秋暂时无法使用[解除人机验证]指令`,
43 | `${at('user')} 很抱歉 小秋权限不足${promiseImage(path.resolve('./assets/images/emoji/我一路向北。离开有你的季节1.jpg'))}`,
44 | `${at('user')} 抱歉 小秋暂时无法为其解除人机验证环节`
45 | ]
46 | }
47 | const fn: CommandFn = originData => {
48 | const { group, other } = originData
49 | // 判断是否已经处于人机验证当中
50 | const haved = inCheckDeadOf(group.id, other.id)
51 | if (!haved) return { items: 1 }
52 | // 否则进行关闭
53 | const id = String(group.id) + String(other.id)
54 | const { token } = persons.get(id)!
55 | clearTimeout(token)
56 | persons.delete(id)
57 | return { items: 2 }
58 | }
59 | const relieveCheckDeadPerson = { fn, sendContent }
60 |
61 | export { relieveCheckDeadPerson }
62 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/delMemberMessage.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [撤回]指令:
3 | * 撤回指定成员的消息,撤回的条数可选,默认为全部
4 | */
5 | import path from 'path'
6 |
7 | import type { SendContent, CommandFn } from '../../lib/interface'
8 |
9 | const sendContent: SendContent = {
10 | name: '撤回',
11 | reg: /^撤回(?\d*)\[CQ:at,qq=(?\d*),text=.*\]\s*$/,
12 | role: 'admin',
13 | member: ({ operations: { at, promiseImage } }) => [
14 | `${at('user')} 想啥呢 撤回指令得管理/群主才可以触发哦~`,
15 | `${at('user')} 抱歉 您暂无撤回权限${promiseImage(path.resolve('./assets/images/emoji/轻轻敲打沉睡的心灵1.jpg'))}`,
16 | `${at('user')} 小秋检测到您暂无撤回权限`
17 | ],
18 | deverDefined: ({ user: { sex }, other: { name }, defined: { count }, operations: { at, face, promiseImage } }) => [
19 | [
20 | `${at('user')} 请输入有效条数`,
21 | `${at('user')} ${sex ? '老弟' : '老妹'},这撤回条数不对啊${promiseImage(path.resolve('./assets/images/emoji/您可蒜了吧1.jpg'))}`,
22 | `${at('user')} 小秋提示您请输入一个合法的撤回条数`
23 | ],
24 | [
25 | `${at('user')} 检测到${name}并没有发送${count}条消息,建议减少要撤回的消息条数`,
26 | `${at('user')} 超载了!${name}并没有发送${count}条消息\n[建议减少要撤回的消息条数]`,
27 | `${at('user')} 撤回条数的太多啦,您可以减少要撤回的消息条数 然后重新进行尝试${promiseImage(path.resolve('./assets/images/emoji/我突然烦你了拜拜1.jpg'))}`
28 | ],
29 | [
30 | `${at('other')} 已撤回您${count}条消息,请注意言辞 文明且积极发言,下次不再警告${face(179)}`,
31 | `${at('other')} 小秋已撤回您${count}条消息,请不要再次发送类似的言论`,
32 | `${at('other')} 您被小秋撤回了${count}条消息,小秋提醒您一定要遵守群规哦~`
33 | ]
34 | ],
35 | equal: ({ user: { name }, operations: { at, face } }) => [
36 | `${at('user')} 对不起 小秋无法帮您撤回消息`,
37 | `${at('other')},${name}要撤回你消息哎${face(174)}`
38 | ],
39 | level: ({ operations: { at } }) => [
40 | `${at('user')} 抱歉 小秋权限不足 暂时不能帮您撤回消息~`,
41 | `${at('user')} 呜呜 小秋权限不足 暂时不能帮您撤回消息~`
42 | ]
43 | }
44 | const fn: CommandFn = async originData => {
45 | const { group, raw_message, reg, operations: { delMsg } } = originData
46 | // 被艾特的人、以及要撤回几条
47 | const { qq, num } = reg.exec(raw_message)?.groups!
48 | const otherId = Number(qq)
49 | // 默认撤回全部
50 | if (num === '') {
51 | const rows = await delMsg(group.id, otherId, 0)
52 | return rows === 0 ? { items: 2, args: { count: 1 } } : undefined
53 | }
54 | // 撤回的条数小于等于0
55 | const row = Number(num)
56 | if (row <= 0) return { items: 1 }
57 | // 正确条数(不考虑当前群聊是否有消息产生,因为在使用撤回指令时,已经产生了消息)
58 | const count = await delMsg(group.id, otherId, row)
59 | // 撤回失败,因为对方没有发送消息
60 | if (count === 0) return { items: 2, args: { count: row } }
61 | // 撤回成功
62 | return { items: 3, args: { count } }
63 | }
64 | const delMemberMessage = { fn, sendContent }
65 |
66 | export { delMemberMessage }
67 |
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/makeSuggestion.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [提建议]指令:
3 | * 提建议指令作为小秋开发者与小秋用户之间一种沟通的桥梁而存在,小秋会将用户提出的建议记录至数据库中,并由小秋开发者来标识当前建议的进度如何
4 | */
5 | import type { SuggestionFormat } from '../../database/forms/interface'
6 | import type { SendContent, CommandFn } from '../../lib/interface'
7 |
8 | const sendContent: SendContent = {
9 | name: '提建议',
10 | reg: /^提建议:/,
11 | role: 'member',
12 | member: ({ user: { name }, operations: { at } }) => [
13 | `${at('user')} 提交失败啦,每条建议都必须要在五个字以上`,
14 | `${name},请尝试重新描述您遇到的问题\n[建议必须要在五个字以上]`,
15 | `${name},本次提交未通过 尝试多输入点内容吧`
16 | ],
17 | admin: ({ operations: { at } }) => [
18 | `${at('user')} 管理大哥 本次提交未通过\n[原因:应至少五个字]`,
19 | `${at('user')} 好管理 建议应至少包含五个字符`,
20 | `${at('user')} 提交失败啦,最少要有五个字哦`
21 | ],
22 | owner: ({ operations: { at } }) => [
23 | `${at('user')} 尊贵的群主大人 您本次提交未通过,原因:应至少五个字`,
24 | `群主大人,建议至少要有五个字哦~ 否则小秋不能帮您提交`,
25 | `${at('user')} 好群主 建议应至少包含五个字符`
26 | ],
27 | deverDefined: ({ user: { name }, operations: { at }, defined: { len, content } }) => [
28 | [
29 | `${at('user')} [提建议]指令的字数最多只能30字哦~`,
30 | `${at('user')} 超过30个字了,小秋记不住呀`,
31 | `${name},抱歉 请尝试缩短建议后重新提交 `
32 | ],
33 | [
34 | `${at('user')} 抱歉 小秋暂不支持此类型的建议`,
35 | `${at('user')} 此建议暂时不能被收纳 请尝试重新整理后再次发送`,
36 | `${at('user')} 小秋暂不能收纳该建议哦`
37 | ],
38 | [
39 | `${at('user')} 您的第${len}条建议『${content}』已被收纳,感谢反馈。`,
40 | `${at('user')} 您的第${len}条建议『${content}』已被收纳,感谢你为小秋的付出~`,
41 | `${at('user')} 灰常感谢,您的第${len}条建议『${content}』已被收纳,开发者看到后会在第一时间进行处理!`
42 | ]
43 | ]
44 | }
45 | // 提建议不分群聊
46 | const fn: CommandFn = async originData => {
47 | const { user: { id }, raw_message, database } = originData
48 | const { getDataBaseData, formSet: { suggestions } } = database
49 | const content = raw_message.slice(4)
50 | if (content.length < 5) return
51 | if (content.length > 30) return { items: 1 }
52 | // 建议中不能包含图片、语音、音视频等(判断标准为CQ码)
53 | const cqReg = /\[CQ:(.*)\]/gi
54 | if (cqReg.test(content)) return { items: 2 }
55 | const sugs = await getDataBaseData(suggestions.name, suggestions.rowSuggestion)(id)
56 | const sug: SuggestionFormat = {
57 | content,
58 | timestamp: +new Date(),
59 | plan: '已反馈给开发同学'
60 | }
61 | const newSugList = sugs ? [...sugs, sug] : [sug]
62 | if (sugs) await getDataBaseData(suggestions.name, suggestions.updateData)(id, newSugList)
63 | else await getDataBaseData(suggestions.name, suggestions.recordRow)(id, newSugList)
64 | return { items: 3, args: { len: newSugList.length, content } }
65 | }
66 | const makeSuggestion = { fn, sendContent }
67 |
68 | export { makeSuggestion }
69 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/banCommon.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [禁言]指令:
3 | * 禁言指定成员,禁言的时间由时间单位决定
4 | */
5 | import path from 'path'
6 |
7 | import { belong } from '../../lib/time'
8 |
9 | import type { SendContent, CommandFn } from '../../lib/interface'
10 |
11 | const sendContent: SendContent = {
12 | name: '禁言',
13 | reg: /^禁言(?\d*)(?.*)\[CQ:at,qq=\d*,text=.*\]\s*$/,
14 | role: 'admin',
15 | member: ({ user: { name }, operations: { at } }) => [
16 | `${at('user')} 想啥呢 禁言指令得管理/群主才可以触发哦~`,
17 | `${at('other')} ${name}要禁言你`,
18 | `${at('user')} 抱歉 您没有禁言权限哦`
19 | ],
20 | deverDefined: ({ user: { name, sex }, defined: { dur, max, unit }, operations: { at, face, promiseImage } }) => [
21 | [
22 | `${at('user')} 最低禁言的时间单位为1`,
23 | `${at('user')} ${sex ? '老弟' : '老妹'},你这单位不太对劲啊${promiseImage(path.resolve('./assets/images/emoji/给你一小巴掌1.jpg'))}`,
24 | `${name},小秋提示您 最低可禁言的时间单位为1`
25 | ],
26 | [
27 | `${at('user')} 怎么还有按年为单位禁言的啊!`,
28 | `${at('user')} 禁言单位不能是年`,
29 | `${at('user')} 禁言${dur}年??${face(291)}`,
30 | `${name},事不能做的太绝 哪有按年禁言的呀`
31 | ],
32 | [
33 | `${at('user')} 单位错误`,
34 | `${name} 单位错了!单位错了!`,
35 | `${at('user')} 小秋检测到您输入的禁言时间单位错误${face(283)}`,
36 | `${at('user')} 小秋暂时还不认识当前时间单位哦~`
37 | ],
38 | [
39 | `${at('user')} 不行~最长禁言时间为${max}${unit}`,
40 | `${name},超过最长禁言时间${max}${unit}了`,
41 | `${at('user')} 小秋提示您最长禁言时间为${max}${unit}${promiseImage(path.resolve('./assets/images/emoji/我突然烦你了拜拜1.jpg'))}`
42 | ]
43 | ],
44 | equal: ({ user: { name: username }, other: { name: othername }, operations: { at, face } }) => [
45 | `${at('user')} 对不起,小秋无法帮您进行禁言${face(187)}`,
46 | `${at('other')} ${username}要禁言你`,
47 | `${at('user')} 别搞事 我可不敢禁言${othername}`
48 | ],
49 | level: ({ operations: { at } }) => [
50 | `${at('user')} 抱歉 小秋权限不足 暂时不能帮您执行禁言操作哦~`,
51 | `${at('user')} 小秋暂时无法为您执行禁言操作`
52 | ]
53 | }
54 | const fn: CommandFn = originData => {
55 | const { group, other, raw_message, reg, operations: { banMember } } = originData
56 | // 禁言时间的单位只能是分钟、小时、秒
57 | const { durStr, unit } = reg.exec(raw_message)?.groups!
58 | const dur = Number.parseFloat(durStr)
59 | if (dur < 1) return { items: 1 }
60 | if (unit === '年') return { items: 2, args: { dur } }
61 | if (unit !== '分钟' && unit !== '小时' && unit !== '天') return { items: 3 }
62 | // 最多只能禁言30天的时间(30天,720小时,43200分钟)
63 | if (dur > belong[unit].max) return { items: 4, args: { max: belong[unit].max, unit } }
64 | banMember(group.id, other.id, unit, dur)
65 | return { noMsg: true }
66 | }
67 | const banCommon = { fn, sendContent }
68 |
69 | export { banCommon }
70 |
--------------------------------------------------------------------------------
/lib/interface/command.ts:
--------------------------------------------------------------------------------
1 | // 有关Oicq的类型,本文件使用any来替代(例如bot、segment、...),具体api请参考其官方文档
2 | ////
3 |
4 | import type { DataBase } from '../../database/interface'
5 | import type { AuthIdType } from '../user'
6 | import type { OicqOperations } from '../oicq/interface'
7 | import type { CommandsName } from '../../database/forms/interface'
8 | import type { GroupchatsId } from './account'
9 | import type { GetObject } from './toolsType'
10 |
11 | // 每个指令的配置项
12 | type Role = 'member' | 'admin' | 'owner'
13 | type LowestRole = 'member' | 'admin' | 'owner'
14 | type GroupAt = (who: 'user' | 'other') => string
15 | type PersonInfo = {
16 | card: string
17 | nickname: string
18 | role: Role
19 | }
20 |
21 | type MemberBasisInfo = {
22 | id: number
23 | sex: boolean
24 | // card、nickname有可能为空,所以使用name兜底
25 | name: string
26 | } & PersonInfo
27 | type OriginMemberBasisInfo = {
28 | user_id: number
29 | sex: string
30 | } & PersonInfo
31 | type GroupBasisInfo = {
32 | id: GroupchatsId
33 | name: string
34 | }
35 |
36 | type InfoTemp = {
37 | user: MemberBasisInfo
38 | other: MemberBasisInfo
39 | defined: GetObject
40 | operations: {
41 | at: GroupAt
42 | face: OicqOperations['face']
43 | promiseImage: OicqOperations['promiseImage']
44 | }
45 | }
46 |
47 | type ReturnsMsg = (infoTemp: InfoTemp) => string[]
48 | type SendContent = {
49 | // 当前指令的名称
50 | name: CommandsName
51 | // 触发该指令的条件
52 | reg: RegExp | RegExp[]
53 | // 能够触发该指令的角色
54 | role: Role | AuthIdType
55 | // 触发者是普通成员
56 | member?: ReturnsMsg
57 | // 触发者是管理员
58 | admin?: ReturnsMsg
59 | // 触发者是群主
60 | owner?: ReturnsMsg
61 | // 自定义参数
62 | deverDefined?: (infoTemp: InfoTemp) => string[][]
63 | // 当存在被执行人时,是否希望检查小秋与被执行人的权限
64 | // 指定了该值,则代表小秋执行当前指令时会校验小秋与被执行人的权限是否相同;若未指定,则代表无需进行相同权限判断
65 | equal?: ReturnsMsg,
66 | // 表示执行该指令时小秋所需的最低身份
67 | // 指定了该值,则代表小秋最低身份必须为admin;若未指定该值,则代表小秋最低身份只需是member即可
68 | level?: ReturnsMsg
69 | }
70 | // 每个指令所对应的处理函数的形参、返回值
71 | type OriginData = {
72 | bot: any
73 | segment: any
74 | user: MemberBasisInfo
75 | other: MemberBasisInfo
76 | group: GroupBasisInfo
77 | raw_message: string
78 | reg: RegExp
79 | operations: OicqOperations
80 | database: DataBase
81 | }
82 | type ReturnConfig = {
83 | items?: number
84 | args?: GetObject
85 | noMsg?: boolean
86 | }
87 | type CommandFn = (
88 | originData: OriginData
89 | ) => undefined | void | ReturnConfig | Promise
90 |
91 | export {
92 | Role,
93 | LowestRole,
94 | GroupAt,
95 | MemberBasisInfo,
96 | OriginMemberBasisInfo,
97 | SendContent,
98 | GroupBasisInfo,
99 | InfoTemp,
100 | OriginData,
101 | CommandFn
102 | }
103 |
--------------------------------------------------------------------------------
/eventsHandle/xiaoqiu/isXiaoQiuOnline.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [小秋小秋]指令:
3 | * 查看小秋是否处于在线状态
4 | */
5 | import { getCurTime, getCurTimeAlias } from '../../lib/time'
6 |
7 | import type { GroupAt } from '../../lib/interface'
8 | import type { SendContent, CommandFn } from '../../lib/interface'
9 |
10 | const male = (at: GroupAt, name: string) => [
11 | `小秋一直在`,
12 | `${at('user')} ${getCurTimeAlias()}`,
13 | `披荆斩棘的${name},你好呀`,
14 | `${at('user')} Hi,你想做我的小迷弟吗?`,
15 | `${at('user')} 兄弟,${getCurTime()}了,你想干啥`,
16 | `${name}哥哥好`,
17 | `小秋暂时不在哦,${getCurTime()}了,眯一会~`
18 | ]
19 | const female = (at: GroupAt, name: string) => [
20 | `${at('user')} 来了来了,请问小秋可以做你的闺蜜吗`,
21 | `${at('user')} 姐姐是想小秋了嘛~`,
22 | `小秋一直在的哦`,
23 | `乘风破浪的${name},你好`,
24 | `${at('user')} 如果小秋会魔法的话,那你会做小秋的迷妹嘛`,
25 | `小秋来啦`
26 | ]
27 | const reg = [
28 | /^小秋小秋$/,
29 | /^在吗小秋$/,
30 | /^你好小秋$/,
31 | /^小秋你好$/,
32 | /^嘿小秋$/,
33 | /^小秋在吗$/,
34 | /^嘿,小秋$/,
35 | /^小秋同学$/,
36 | /^秋总$/,
37 | /^小秋$/,
38 | /^小小秋$/,
39 | /^秋秋$/
40 | ]
41 | const admin = (at: GroupAt, name: string, sex: boolean) => {
42 | const maleFn = () => {
43 | const arrs = male(at, name)
44 | const text = [
45 | `${at('user')} 管理哥哥,小秋一直在这里哦`,
46 | `${at('user')} 管理哥哥你好,我叫小秋~`,
47 | `${at('user')} 小秋好想拥有一个身为管理员的小迷弟哦~`
48 | ]
49 | return [...arrs, ...text]
50 | }
51 | const femaleFn = () => {
52 | const arrs = female(at, name)
53 | const text = [
54 | `${at('user')} 管理姐姐,小秋会一直陪伴你哦`,
55 | `${at('user')} 管理姐姐你好,我叫小秋,你可以叫我秋秋哦`,
56 | `${at('user')} 小秋也想拥有一个身为管理员的小迷妹哦~`
57 | ]
58 | return [...arrs, ...text]
59 | }
60 | const result = sex ? maleFn() : femaleFn()
61 | return result
62 | }
63 | const owner = (at: GroupAt, name: string, sex: boolean) => {
64 | const maleFn = () => {
65 | const arrs = male(at, name)
66 | const text = [
67 | `${at('user')} 群主大人,小秋一直在哦`,
68 | `${at('user')} 如果群主是小秋的小迷弟就好咯~`
69 | ]
70 | return [...arrs, ...text]
71 | }
72 | const femaleFn = () => {
73 | const arrs = female(at, name)
74 | const text = [
75 | `${at('user')} 群主姐姐,小秋会一直陪伴你哦`,
76 | `${at('user')} 如果群主是小秋的小迷妹就太棒啦~`
77 | ]
78 | return [...arrs, ...text]
79 | }
80 | const result = sex ? maleFn() : femaleFn()
81 | return result
82 | }
83 | const sendContent: SendContent = {
84 | name: '小秋你好',
85 | reg,
86 | role: 'member',
87 | member: ({ user: { name, sex }, operations: { at } }) => sex ? male(at, name) : female(at, name),
88 | admin: ({ user: { name, sex }, operations: { at } }) => admin(at, name, sex),
89 | owner: ({ user: { name, sex }, operations: { at } }) => owner(at, name, sex)
90 | }
91 | const fn: CommandFn = () => { }
92 | const isXiaoQiuOnline = { fn, sendContent }
93 |
94 | export { isXiaoQiuOnline }
95 |
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/listenMusic.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [听歌]指令:
3 | * 使用网易云Api,以QQ语音的格式(song-id.amr)发送用户所指定的歌曲
4 | * (音频转换时可能会出现音质破损)
5 | */
6 | import fs from 'fs'
7 | import request from 'request'
8 |
9 | import type { MusicId, MusicUrl } from './interface'
10 | import type { SendContent, CommandFn } from '../../lib/interface'
11 |
12 | const sendContent: SendContent = {
13 | name: '听歌',
14 | reg: /^\s*听(?.*)\s*$/,
15 | role: 'member',
16 | deverDefined: ({ defined: { songName }, operations: { at } }) => [
17 | [
18 | `${at('user')} 哎呀 讨厌!歌名当然不允许为空啦`,
19 | `${at('user')} 哼 你没有输入歌曲名,那小秋就不会唱给你听喽~`,
20 | `${at('user')} 请输入一个歌曲名称`
21 | ],
22 | [
23 | `${at('user')} 稍等稍等 小秋唱的有点口渴啦~`,
24 | `${at('user')} 让小秋喝完这口水再唱吧`,
25 | `${at('user')} 小秋好累呀 可以不唱了嘛~`
26 | ],
27 | [
28 | `${at('user')} 您点的《${songName}》来喽~`,
29 | `${at('user')} 小秋录制了一首您点的《${songName}》,快来听听吧~`,
30 | `${at('user')} 不得不说《${songName}》唱的好棒呀~`
31 | ],
32 | [
33 | `${at('user')} 哎呀 网络好像开小差了,没有点到《${songName}》`,
34 | `${at('user')} 小秋发现网络好像芭比Q了,《${songName}》没有成功播放,不过您可以再试一次哦~`
35 | ]
36 | ]
37 | }
38 |
39 | const lh = 'http://localhost:3000'
40 | const writeFileFromRequest = (url: string, localUrl: string) =>
41 | new Promise(r => {
42 | const ws = fs.createWriteStream(localUrl)
43 | request(url).pipe(ws)
44 | ws.on('close', r)
45 | })
46 | const getMusicId = (url: string): Promise =>
47 | new Promise(r => request(`${lh}${url}`, (_: unknown, __: unknown, data: string) => r(JSON.parse(data))))
48 | const getMusicUrl = (url: string): Promise =>
49 | new Promise(r => request(`${lh}${url}`, (_: unknown, __: unknown, data: string) => r(JSON.parse(data))))
50 |
51 | const fn: CommandFn = originData =>
52 | new Promise(async res => {
53 | const { bot, group, raw_message, segment, reg } = originData
54 | const songName = reg.exec(raw_message)?.groups?.name!
55 | if (songName === '') return res({ items: 1 })
56 | const dataId = await getMusicId(`/search?keywords=${encodeURI(songName)}`)
57 | if (dataId.code !== 200) return res({ items: 2 })
58 | const id = dataId.result.songs[0].id
59 | const dataUrl = await getMusicUrl(`/song/url?id=${id}`)
60 | if (dataUrl.code !== 200) return res({ items: 2 })
61 | const url = dataUrl.data[0].url
62 | const filename = `./assets/videos/listen/${id}.mp3`
63 | await writeFileFromRequest(url, filename)
64 | fs.readFile(filename, (_: NodeJS.ErrnoException | null, data: Buffer) => {
65 | try {
66 | const video = segment.record(Buffer.from(data))
67 | bot.sendGroupMsg(group.id, video)
68 | res({ items: 3, args: { songName } })
69 | } catch (e) {
70 | res({ items: 4, args: { songName } })
71 | }
72 | })
73 | })
74 | const listenMusic = { fn, sendContent }
75 |
76 | export { listenMusic }
77 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 小秋(xiaoqiu)
2 |
3 | ## 项目介绍
4 |
5 | 机器人基于库OICQ进行开发,其菜单可分为五个部分,即群管理操作、群聊相关、小秋相关、积分玩法、其它玩法。每个部分均由若干指令构成,每个指令又有独自的配置项,例如指令名称、返回内容、触发其所需的最低权限等,且每个指令的触发方式有多种(作为隐藏彩蛋),成员可通过\[菜单\]指令获取更加具体的玩法
6 |
7 | ## 生成项目
8 |
9 | 克隆至本地
10 |
11 | ```javascript
12 | git clone https://github.com/777miss/xiao-qiu.git
13 | ```
14 |
15 | 在根目录`/xiao-qiu`中安装其声明文件
16 |
17 | ```javascript
18 | npm i -D
19 | ```
20 |
21 | 由于账号所对应的数据问题,需要将`xiao-qiu/lib`下的`user-copy`文件夹名称修改为`user`(`user`文件中所需的数据可稍后进行配置),随后在`xiao-qiu`中编译TS文件
22 |
23 | ```javascript
24 | tsc
25 | ```
26 |
27 | 使用`tsc`命令编译后会生成`xiaoqiu`新文件夹,部署时只需部署该文件夹即可
28 |
29 | ## 准备工作
30 |
31 | 按照上述步骤生成项目后,目录结构为
32 |
33 | ```
34 | ├─xiao-qiu // 开发环境
35 | | ├─assets
36 | | ├─database
37 | | ├─eventHandle
38 | | ├─lib
39 | | ├─temporary
40 | | ├─timing
41 | | ├─xiaoqiu // 使用tsc命令生成的js文件所在处
42 | | ├─......
43 | | ├─app.ts
44 | ```
45 |
46 | 其`xiao-qiu/assets`文件夹结构如下
47 | ```
48 | ├─assets
49 | | ├─images
50 | | ├─readme
51 | | ├─videos
52 | | ├─package.json
53 | | ├─xiaoqiu_alpha.sql
54 | ```
55 |
56 | 将`xiao-qiu/assets`文件夹整体移至`xiao-qiu/xiaoqiu`文件夹下,并将`xiao-qiu/xiaoqiu/assets`中的`pakcage.json`文件移至`xiao-qiu/xiaoqiu`目录下,新的`xiao-qiu/xiaoqiu`目录结构为
57 |
58 | ```
59 | ├─xiaoqiu
60 | | ├─assets
61 | | | └images
62 | | | └readme
63 | | | └videos
64 | | | └xiaoqiu_alpha.sql
65 | | ├─......
66 | | ├─app.js
67 | | ├─package.json
68 | ```
69 |
70 | 随后在`xiao-qiu/xiaoqiu`下安装所需依赖
71 |
72 | ```javascript
73 | npm i
74 | ```
75 |
76 | ## 账号相关
77 |
78 | `xiao-qiu/lib/user`中存在`account.ts`与`account-alpha.ts`两个文件,前者为正式版配置,后者为测试版配置,可以在`xiao-qiu/lib/user/index.ts`中根据不同的需要进行切换
79 |
80 | 以`account-alpha.ts`为例,用户必须指定机器人的登录账号与密码、所监听的群聊账号、要修改群昵称的群聊账号、绝对权限、以及数据库配置
81 |
82 | 绝对权限是相对于指令的叫法,对于用户来说,绝对权限即特定用户,是独立于`member(普通成员)`、`admin(管理员)`、`owner(群主)`之外的用户,若某个指令指定了其所需的最低触发权限为特定用户,则只有该特定用户可触发,判断规则是账号层面的判断,而不是权限层面的判断
83 |
84 | 数据库配置方面,您可以选择新建一个数据库,并运行`xiao-qiu/xiaoqiu/assets/xiaoqiu_alpha.sql`文件,创建其所需数据结构(项目启动时会自动初始化表数据,所以只需关心它们的结构,而非数据),然后在`connectionDbConfig`中指定刚才创建好的数据库相关数据,例如
85 |
86 | ```javascript
87 | const connectionDbConfig = {
88 | host: '主机名',
89 | user: '数据库账号',
90 | password: '数据库密码',
91 | database: '数据库名称'
92 | }
93 | ```
94 |
95 | > 在修改`account-alpha.ts`文件之后,应当重新执行`tsc`命令
96 |
97 | ## 启动项目
98 |
99 | 完成以上步骤,便可在`xiao-qiu/xiaoqiu`下启动该项目
100 |
101 | ```javascript
102 | node app
103 | ```
104 |
105 | 初次登陆时,您可能会收到一条滑动验证,此时需要在该验证地址中得到`token`,然后将其输入在终端窗口中即可成功登陆(在`network`中找到对应请求地址)
106 |
107 | > 登录后,`oicq`会在您的`xiao-qiu/xiaoqiu`下自动生成`data`文件夹,用于存放账号相关数据
108 |
109 | ## 开发新指令
110 |
111 | > 只要您修改了源代码,则必须通过`tsc`命令生成新的JS文件,并`node app`运行它们
112 |
113 | 由于每个指令配置项众多,所以您可以参考`xiao-qiu/lib/Temp/temp-comment.ts`进行开发,或者您可以选择复制一份`xiao-qiu/lib/Temp/temp.ts`文件至您的新指令所在处
114 |
115 | 开发新指令时您应当注意:
116 |
117 | 1. 在`xiao-qiu/database/forms/interface.ts`中新增一个指令名称
118 | 2. 在`xiao-qiu/eventHandle/fn.ts`中引入新指令
119 | 3. 确保数据库中`switch_commands`中该指令为开启状态,否则不会被触发
120 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/setGroupWholeBan.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [全员禁言]指令:
3 | * 开启/关闭全员禁言,禁言的单位同[禁言]指令相同
4 | */
5 | import { belong, secToFormat, getWholeDate } from '../../lib/time'
6 |
7 | import type { SendContent, CommandFn } from '../../lib/interface'
8 |
9 | const sendContent: SendContent = {
10 | name: '全员禁言',
11 | reg: /^(开启|关闭)全员禁言(?\d*)(?.*)$/,
12 | role: 'admin',
13 | member: ({ operations: { at } }) => [
14 | `${at('user')} 造反啊?小小成员 可笑可笑`,
15 | `${at('user')} 这全员禁言 说开就开?`,
16 | `${at('user')} 您无此权限~`
17 | ],
18 | deverDefined: ({ user: { sex }, defined, operations: { at } }) => [
19 | [
20 | `${at('user')} 全员禁言已被关闭`,
21 | `${at('user')} 小秋已关闭全员禁言~`,
22 | `${at('user')} ,${sex ? '哥哥' : '姐姐'} 小秋已经把全员禁言关闭了`
23 | ],
24 | [
25 | `${at('user')} 哎呀 最低全员禁言时间单位为1哦~`,
26 | `${at('user')} 小秋检测到了非法的时间单位,请重新尝试`,
27 | `${at('user')} 呜呜呜 小秋还不认识当前的时间单位哦~`
28 | ],
29 | [
30 | `${at('user')} 全员禁言${defined.dur}年,还让不让群友活了啊`,
31 | `${at('user')} 小秋不能帮您进行全员禁言${defined.dur}年`,
32 | `${at('user')} 全员禁言${defined.dur}年,这不符合美少女小秋的风格哦~`
33 | ],
34 | [
35 | `${at('user')} 请输入正确的时间单位`,
36 | `${at('user')} 小秋提示您 请输入正确的时间单位`,
37 | `${at('user')} 哎呀 小秋还不认识这个时间单位呢~`
38 | ],
39 | [
40 | `${at('user')} 最长可全员禁言时间为${defined.bMax}${defined.unit}`,
41 | `${at('user')} 小秋提醒您,最长可全员禁言时间为${defined.bMax}${defined.unit}`,
42 | `${at('user')} 呜呜 小秋最多只能帮您全员禁言${defined.bMax}${defined.unit}哦~`
43 | ],
44 | [
45 | `${at('user')} 芜湖 您已开启全员禁言${defined.time}\n${defined.nextTime}`,
46 | `小秋已开启全员禁言${defined.time}\n${defined.nextTime}`
47 | ]
48 | ],
49 | level: ({ operations: { at } }) => [
50 | `${at('user')} 抱歉 小秋权限不足 暂时不能帮您执行全员禁言操作哦~`,
51 | `${at('user')} 呜呜 小秋权限不足 暂时不能帮您执行全员禁言操作哦~`
52 | ]
53 | }
54 | const fn: CommandFn = originData => {
55 | const { bot, group, raw_message, reg } = originData
56 | // oicq1.x中无法得知当前是否处于全员禁言状态
57 | if (/^关闭全员禁言/gi.test(raw_message)) {
58 | bot.setGroupWholeBan(group.id, false)
59 | return { items: 1 }
60 | }
61 | const { dur, unit } = reg.exec(raw_message)?.groups!
62 | const durNum = Number.parseFloat(dur)
63 | if (durNum < 1) return { items: 2 }
64 | if (unit === '年') return { items: 3, args: { dur } }
65 | if (unit !== '分钟' && unit !== '小时' && unit !== '天') return { items: 4 }
66 | // 最多只能禁言30天的时间(30天,720小时,43200分钟)
67 | const bMax = belong[unit].max
68 | const bSec = belong[unit].sec(durNum)
69 | const bMs = bSec * 1000
70 | if (durNum > bMax) return { items: 5, args: { bMax, unit } }
71 | bot.setGroupWholeBan(group.id, true)
72 | const nextTime = getWholeDate(new Date(+new Date() + bMs))
73 | setTimeout(() => {
74 | bot.setGroupWholeBan(group.id, false)
75 | }, bMs)
76 | return { items: 6, args: { time: secToFormat(bSec), nextTime: `[${nextTime}]自动关闭` } }
77 | }
78 | const setGroupWholeBan = { fn, sendContent }
79 |
80 | export { setGroupWholeBan }
81 |
--------------------------------------------------------------------------------
/database/forms/interface.ts:
--------------------------------------------------------------------------------
1 | import type { CommandsConfig } from '../../eventsHandle/fns'
2 |
3 | // groups_config表中的群配置
4 | type GroupEvents = 'message' | 'increase' | 'ban' | 'decrease' | 'poke'
5 | type GroupsConfig = {
6 | draw: {
7 | discount: 5 | 6 | 7 | 8 | 9
8 | timestamp: number
9 | }
10 | setCard: {
11 | isAuto: boolean
12 | content: string
13 | }
14 | events: {
15 | [K in GroupEvents]: boolean
16 | }
17 | score: {
18 | dailyLimit: number
19 | }
20 | }
21 | // 存储单条群聊消息的格式(在群聊中每产出一条消息,均按照此固定格式存储)
22 | type PreGroupchatMessage = {
23 | userId: number
24 | msgId: string
25 | timestamp: number
26 | content: string
27 | }
28 | // suggestions表中所存储的每条建议的格式
29 | type SuggestionFormat = {
30 | content: string
31 | timestamp: number
32 | plan: string,
33 | result?: string
34 | }
35 | type SuggestionsWhole = {
36 | user_id: number
37 | suggestion: string // SuggestionFormat[]
38 | }
39 | type SuggestionsWholeFormat = SuggestionsWhole[]
40 | // switch_commands表中所存储的指令名称
41 | /* type CommandsName = CommandsConfig[keyof CommandsConfig] extends {
42 | fn: any,
43 | sendContent: {
44 | name: `${infer U}`,
45 | [k: string]: any
46 | }
47 | } ? `${U}` : never */
48 | type CommandsName =
49 | | '问题格式'
50 | | '签到'
51 | | '人机验证'
52 | | '解除人机验证'
53 | | '禁止发言'
54 | | '解除禁止发言'
55 | | '改群昵称'
56 | | '警告'
57 | | '禁言'
58 | | '撤回'
59 | | '踢'
60 | | '全员禁言'
61 | | 'js'
62 | | '搜掘金'
63 | | '文案'
64 | | '查建议'
65 | | '早'
66 | | '晚'
67 | | '斗地主'
68 | | '听歌'
69 | | '提建议'
70 | | '在线聊天'
71 | | '抽奖'
72 | | '发放积分'
73 | | '我的背包'
74 | | '道具卡'
75 | | '抽奖限时折扣'
76 | | '查询限时折扣'
77 | | '获取积分方式'
78 | | '小秋你好'
79 | | '菜单'
80 | | '切换指令'
81 | | '小秋版本介绍'
82 | type GroupsSwitchCommands = {
83 | [k in CommandsName]: boolean
84 | }
85 | // user_packsack表中所存储的用户背包格式
86 | type UserPacksack = {
87 | score: number
88 | curInc: number
89 | card: {
90 | ban: number
91 | delMsg: number
92 | immune: number
93 | see: number
94 | }
95 | }
96 | type GroupsUserPacksack = {
97 | [k: string]: UserPacksack
98 | }
99 |
100 | type DataBaseBasisField = {
101 | recordRow: never
102 | updateData: never
103 | }
104 | type ProcessField = T & DataBaseBasisField
105 |
106 | type Groups_config = ProcessField<{
107 | retrieveData: GroupsConfig
108 | }>
109 | type Suggestions = ProcessField<{
110 | rowSuggestion: [SuggestionFormat] | undefined
111 | retrieveData: SuggestionsWholeFormat
112 | }>
113 | type Switch_commands = ProcessField<{
114 | retrieveData: GroupsSwitchCommands
115 | }>
116 | type User_packsack = ProcessField<{
117 | retrieveData: GroupsUserPacksack
118 | }>
119 |
120 | type DataBaseDataType = {
121 | groups_config: Groups_config
122 | suggestions: Suggestions
123 | switch_commands: Switch_commands
124 | user_packsack: User_packsack
125 | }
126 |
127 | export {
128 | GroupEvents,
129 | CommandsName,
130 | UserPacksack,
131 | GroupsUserPacksack,
132 | GroupsSwitchCommands,
133 | SuggestionFormat,
134 | PreGroupchatMessage,
135 | GroupsConfig,
136 |
137 | DataBaseDataType
138 | }
139 |
--------------------------------------------------------------------------------
/eventsHandle/xiaoqiu/switchCommand.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [开启/关闭指令]指令:
3 | * 用于切换某个指令的状态,当某个指令被关闭后,再次尝试触发时,小秋不会作出任何回应
4 | */
5 | import { AuthId, GroupAt } from '../../lib/interface'
6 | import type { CommandsName } from '../../database/forms/interface'
7 | import type { SendContent, CommandFn } from '../../lib/interface'
8 |
9 | const member = (sex: boolean, name: string, at: GroupAt) => {
10 | const male = [
11 | `${at('user')} 小秋收到的指示是,当前指令只能由长的最帅的人触发哦~`,
12 | `${name}哥哥,您不能使用当前指令`,
13 | `${at('user')} 听说颜值在30以下的哥哥是不能触发该指令的哦`
14 | ]
15 | const female = [
16 | `很抱歉,乘风破浪的${name}姐姐,小秋不能帮您使用该指令`,
17 | `${name},啊这 我的小迷妹也喜欢用这个指令吗`,
18 | `${at('user')} 当前指令只能由颜值99.999以下的小姐姐触发哦~`
19 | ]
20 | return sex ? male : female
21 | }
22 | const admin = (sex: boolean, at: GroupAt) => {
23 | const male = [
24 | `${at('user')} 想要触发当前指令,光是管理员可不够,还不能做个小小的男同哦~`,
25 | `${at('user')} 兄弟 这个指令就算是管理也不能用哦~`,
26 | `${at('user')} 等你成为群主了再来试着触发当前指令吧~`
27 | ]
28 | const female = [
29 | `${at('user')} 呜呜 好姐姐,这个指令太丑了,您不能使用它`,
30 | `${at('user')} 小秋认为您如果使用这个指令,会降低你的管理员姐姐身份哦~`,
31 | `${at('user')} 尊贵的管理姐姐,您不能使用当前指令`
32 | ]
33 | return sex ? male : female
34 | }
35 | const sendContent: SendContent = {
36 | name: '切换指令',
37 | reg: [
38 | /^(开启|关闭)指令[(?.*)]$/,
39 | /^(开启|关闭)[(?.*)]指令$/,
40 | /^(开启|关闭)(?.*)指令$/
41 | ],
42 | role: [AuthId.FuncJin],
43 | member: ({ user: { name, sex }, operations: { at } }) => member(sex, name, at),
44 | admin: ({ user: { sex }, operations: { at } }) => admin(sex, at),
45 | owner: ({ user: { name, sex }, operations: { at } }) => member(sex, name, at),
46 | deverDefined: ({ operations: { at }, defined: { text, state } }) => [
47 | [
48 | `${at('user')} 小秋没有查询到您所输入的指令`,
49 | `${at('user')} 您输入的指令无效,请重新输入后再次尝试`,
50 | `${at('user')} 小秋发现没有当前指令,所以无法对其进行操作哦`
51 | ],
52 | [
53 | `${at('user')} 哎呀 小秋没有糊涂哦,当前指令已经是关闭状态啦`,
54 | `${at('user')} 小秋认为已经关闭的指令就不需要再次关闭了`,
55 | `${at('user')} 咦 是在考验小秋吗?当前指令已经是关闭状态了哦`
56 | ],
57 | [
58 | `${at('user')} 哎呀 小秋没有糊涂哦,当前指令已经是开启状态啦`,
59 | `${at('user')} 小秋认为已经开启的指令就不需要再次开启了,嘻嘻`,
60 | `${at('user')} 咦 是在考验小秋吗?当前指令已经是开启状态了哦`
61 | ],
62 | [
63 | `${at('user')} 小秋帮您把${text}变为了${state}状态哦`,
64 | `${at('user')} nice!小秋已经把${text}改为了${state}状态`,
65 | `${at('user')} 小秋将${text}改为了${state}状态,还不快夸夸人家`
66 | ]
67 | ]
68 | }
69 | const fn: CommandFn = async originData => {
70 | const { group, raw_message, reg, database } = originData
71 | const { getDataBaseData, formSet: { switch_commands } } = database
72 | const text = reg.exec(raw_message)?.groups?.who! as CommandsName
73 | const state = /开启/g.test(raw_message)
74 | const whole = await getDataBaseData(switch_commands.name, switch_commands.retrieveData)(group.id)
75 | const cur = whole[text]
76 | if (cur === undefined) return { items: 1 }
77 | if (state === false && cur === false) return { items: 2 }
78 | if (state === true && cur === true) return { items: 3 }
79 | whole[text] = !cur
80 | await getDataBaseData(switch_commands.name, switch_commands.updateData)(group.id, whole)
81 | return { items: 4, args: { text: `[${text}]`, state: state ? '开启' : '关闭' } }
82 | }
83 | const switchCommand = { fn, sendContent }
84 |
85 | export { switchCommand }
86 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/setCard/setCard.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [改群昵称]指令:
3 | * 修改指定成员的群昵称。由于群昵称可遵循的规则较多,但小秋采用的是 地区简称-性别-昵称 形式,其修改的规则或稍有不同
4 | */
5 | import path from 'path'
6 |
7 | import {
8 | isCardRule,
9 | getChangeAllCard,
10 | getChangeProvinceCard,
11 | getChangeSexCard,
12 | getChangeNicknameCard
13 | } from './tools'
14 |
15 | import type { SendContent, CommandFn } from '../../../lib/interface'
16 |
17 | const sendContent: SendContent = {
18 | name: '改群昵称',
19 | reg: [
20 | /^改群昵称\[CQ:at,qq=\w*,text=@(?.*)\]\s*$/,
21 | /^修改群昵称\[CQ:at,qq=\w*,text=@(?.*)\]\s*$/,
22 | /^换群昵称\[CQ:at,qq=\w*,text=@(?.*)\]\s*$/
23 | ],
24 | role: 'admin',
25 | member: ({ other: { name }, operations: { at, promiseImage } }) => [
26 | `${at('user')} 对不起 您的权限不足以修改${name}的群昵称`,
27 | `${at('user')} 很抱歉 小秋检测到您没有修改${name}群昵称的权限${promiseImage(path.resolve('./assets/images/emoji/万用表情/1.jpg'))}`,
28 | `${at('user')} 当前指令只能由管理/群主去使用哦~`
29 | ],
30 | deverDefined: ({ defined: { card, result }, operations: { at, face } }) => [
31 | [
32 | `${at('user')} 检测到<${card}>符合我群已制定的群昵称规则,因此无需更改`,
33 | `${at('user')} 抱歉 小秋暂时无法对已经符合群昵称规则的昵称进行更改`,
34 | `${at('user')} 小秋检测到群昵称<${card}>符合规则,因此无需更改${face(174)}`
35 | ],
36 | [
37 | `${at('other')} 小秋检测到群昵称<${card}>不符合我群已制定的规则,现已将其更改为<${result}>`,
38 | `${at('other')} 因为<${card}>不符合本群所制定的昵称规则,因此我已将其更改为<${result}>`,
39 | `${at('other')} 小秋已将您的原群昵称<${card}>更改为<${result}>`
40 | ],
41 | [
42 | `${at('other')} 小秋检测到群昵称<${card}>中的省份不符合规则,现已将其修改为<${result}>`,
43 | `${at('other')} 因为<${card}>的省份不符合本群所制定的昵称规则,因此我已将其修改为<${result}>`,
44 | `${at('other')} 小秋已将<${card}>中的省份给您修改为<${result}>`
45 | ],
46 | [
47 | `${at('other')} 小秋检测到群昵称<${card}>中的性别不符合规则,现已将其修改为<${result}>`,
48 | `${at('other')} 因为<${card}>的性别不符合本群所制定的昵称规则,因此我已将其修改为<${result}>`,
49 | `${at('other')} 小秋已将<${card}>中的性别给您修改为<${result}>`
50 | ],
51 | [
52 | `${at('other')} 小秋检测到群昵称<${card}>中的名称不符合规则,现已将其修改为<${result}>`,
53 | `${at('other')} 因为<${card}>的名称不符合本群所制定的昵称规则,因此我已将其修改为<${result}>`,
54 | `${at('other')} 小秋已将<${card}>中的名称给您更改为<${result}>`
55 | ]
56 | ],
57 | equal: ({ other: { name }, operations: { at } }) => [
58 | `${at('user')} 抱歉 小秋无法修改对方群昵称`,
59 | `小秋暂时无法修改${name}的群昵称`,
60 | `${at('user')} 别对自己人修改群昵称呀`
61 | ],
62 | level: ({ other: { name } }) => [
63 | `小秋权限不够哦,无法修改${name}的群昵称`,
64 | `很抱歉 小秋暂无此权限`,
65 | `抱歉 小秋暂时无法使用该指令去修改${name}的群昵称`
66 | ]
67 | }
68 | const bar = [
69 | {
70 | type: 'none',
71 | getCard: getChangeAllCard,
72 | items: 2
73 | },
74 | {
75 | type: 'province',
76 | getCard: getChangeProvinceCard,
77 | items: 3
78 | },
79 | {
80 | type: 'sex',
81 | getCard: getChangeSexCard,
82 | items: 4
83 | },
84 | {
85 | type: 'nickname',
86 | getCard: getChangeNicknameCard,
87 | items: 5
88 | }
89 | ]
90 | // 修改用户群昵称
91 | const fn: CommandFn = async originData => {
92 | const { bot, group, other: { id, card, nickname } } = originData
93 | const { flag, reason } = await isCardRule(bot, group.id, id)
94 | // 群昵称符合规则
95 | if (flag) return { items: 1, args: { card } }
96 | const { getCard, items } = bar.find(({ type }) => type === reason)!
97 | const result = await getCard(bot, group.id, id)
98 | bot.setGroupCard(group.id, id, result)
99 | return { items, args: { card: card ? card : nickname, result } }
100 | }
101 | const setCard = { fn, sendContent }
102 |
103 | export { setCard }
104 |
--------------------------------------------------------------------------------
/lib/time/timeUnitTransform.ts:
--------------------------------------------------------------------------------
1 | import { isDecimals } from '../methods'
2 | import { UnitEng } from './interface'
3 |
4 | // 转为秒
5 | const belong = {
6 | 分钟: {
7 | sec: (dur: number): number => dur * 60,
8 | max: 43200
9 | },
10 | 小时: {
11 | sec: (dur: number): number => dur * 60 * 60,
12 | max: 720
13 | },
14 | 天: {
15 | sec: (dur: number): number => dur * 60 * 60 * 24,
16 | max: 30
17 | }
18 | }
19 | const transformTimeNameAlias = { mins: '分钟', hours: '小时', days: '天' } as const
20 | const transformTimeNameEn = { 分钟: 'mins', 小时: 'hours', 天: 'days' }
21 | // 得到具体的分钟(例如,传入121分钟,返回1分钟)
22 | const getMins = (mins: number): number => {
23 | const result = mins - 60
24 | return result < 60 ? result : getMins(result)
25 | }
26 | // 得到具体的小时(例如,传入25小时,返回1小时)
27 | const getHours = (Hours: number): number => {
28 | const result = Hours - 24
29 | return result < 24 ? result : getHours(result)
30 | }
31 | // 秒转为对应的分钟、小时、天数
32 | const getIntegerTime = (num: number) => {
33 | if (isDecimals(num)) return num.toFixed(2)
34 | return num
35 | }
36 | const secToFormat = (dur: number): string => {
37 | const mins = dur / 60
38 | if (mins < 60) return `${getIntegerTime(mins)}分钟`
39 | const hours = mins / 60
40 | if (hours < 24) return `${getIntegerTime(hours)}小时${getIntegerTime(getMins(mins))}分钟`
41 | const days = hours / 24
42 | return `${getIntegerTime(days)}天${getIntegerTime(getHours(hours))}小时${getIntegerTime(getMins(mins))}分钟`
43 | }
44 | // 得到对应的日期(2022/06/28)
45 | const process = (time: number): string => (time < 10 ? `0${time}` : `${time}`)
46 | const getDate = (date: Date): string => {
47 | const year = date.getFullYear()
48 | const month = process(date.getMonth() + 1)
49 | const day = process(date.getDate())
50 | return `${year}/${month}/${day}`
51 | }
52 | // 得到对应的日期(2022/06/28 15:21)
53 | const getWholeDate = (date: Date): string => {
54 | const hour = process(date.getHours())
55 | const mins = process(date.getMinutes())
56 | return `${getDate(date)} ${hour}:${mins}`
57 | }
58 | // 得到当前的时间,例如 13:01
59 | const getCurTime = (): string => {
60 | const time = new Date()
61 | const hours = process(time.getHours())
62 | const mins = process(time.getMinutes())
63 | return `${hours}:${mins}`
64 | }
65 | // 返回当前时间段的别名,例如 08:00 为 早上好
66 | const getCurTimeAlias = (): string => {
67 | const frame = [
68 | [6, '夜深了,快睡吧'],
69 | [9, '早上好'],
70 | [12, '中午好'],
71 | [18, '下午好'],
72 | [23, '晚上好']
73 | ]
74 | const time = new Date()
75 | const hours = time.getHours()
76 | const result = frame.find(v => hours <= v[0])!
77 | return `${result[1]}`
78 | }
79 | // 传入指定的时间段,返回当前时间段所对应的时间戳
80 | // 例如当前时间为:2022/08/16 14:17
81 | // 获取三天后的时间戳,即返回2022/08/19 14:17 所对应的时间戳
82 | type timeInfo = {
83 | mins: number
84 | hours: number
85 | days: number
86 | }
87 | const getAssignTimestamp = (timeInfo: timeInfo) => +new Date() + getSpecificTimeMS(timeInfo)
88 | // 得到分钟、小时、天数,所对应的毫秒数
89 | const getSpecificTimeMS = (timeInfo: timeInfo) => {
90 | const keys = Object.keys(timeInfo) as UnitEng[]
91 | let ms = 0
92 | keys.forEach(t => {
93 | const timestamp = belong[transformTimeNameAlias[t]].sec(timeInfo[t]) * 1000
94 | ms += timestamp
95 | })
96 | return ms
97 | }
98 | // 得到当前是白天还是黑天
99 | // true代表白天,false代表黑天
100 | const getCurDarkOrLights = () => {
101 | const hours = new Date().getHours()
102 | return hours >= 6 && hours < 19
103 | }
104 |
105 | export {
106 | belong,
107 | secToFormat,
108 | getDate,
109 | getWholeDate,
110 | getCurTime,
111 | getCurTimeAlias,
112 | getAssignTimestamp,
113 | transformTimeNameAlias,
114 | transformTimeNameEn,
115 | getSpecificTimeMS,
116 | getCurDarkOrLights
117 | }
118 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/setCard/tools.ts:
--------------------------------------------------------------------------------
1 | import {
2 | areaAbbr,
3 | sexArr,
4 | sexBelong,
5 | keywordsReg,
6 | areaAll,
7 | randomProvince,
8 | formSpecificProvinceToShort
9 | } from '../../../lib/groupchat'
10 |
11 | import type {
12 | Province,
13 | BeAtInfoGroupSex,
14 | BeAtInfoGroupCard,
15 | ChangeCard,
16 | IsCardRule
17 | } from './interface'
18 |
19 | // 拆分用户的群昵称
20 | const getSpecificCard = (card: string) => {
21 | const result = card.split('-')
22 | return result.slice(0, 3)
23 | }
24 | // 判断群昵称是否符合规则,如果不符合规则,则返回false;如果符合规则,则返回true
25 | const isCardRule: IsCardRule = async (bot, gId, uId) => {
26 | const beAtInfoGroup = await bot.getGroupMemberInfo(gId, uId)
27 | const { card } = beAtInfoGroup.data
28 | const reg = /^(?.*)-(?.*)-(?.*)\s*$/
29 | // 根本不符合
30 | if (!reg.test(card)) return { flag: false, reason: 'none' }
31 | const name = reg.exec(card)?.groups!
32 | const { province, sex, nickname } = name as Province
33 | // 省份不符合
34 | if (!areaAbbr[province]) return { flag: false, reason: 'province' }
35 | // 性别不符合
36 | if (!sexArr.includes(sex)) return { flag: false, reason: 'sex' }
37 | const isNoKeys = keywordsReg.test(nickname)
38 | // 昵称中包含敏感词汇,不符合
39 | if (isNoKeys) return { flag: false, reason: 'nickname' }
40 | return { flag: true, reason: '符合' }
41 | }
42 | // 避免用户的网名为(_ - 空格)
43 | const nicknameSymbols = ['_', '-', ' ', '', '.']
44 | // 获取修改后的某位用户的整体群昵称(省份、性别、昵称,全部修改)
45 | const getChangeAllCard: ChangeCard = async (bot, groupId, userId) => {
46 | const beAtInfoGroup: BeAtInfoGroupCard = await bot.getGroupMemberInfo(groupId, userId)
47 | const { area, sex: curSex, nickname: curNickname } = beAtInfoGroup.data
48 | const r_area_all_keys = Object.keys(areaAll)
49 | const curProvince = r_area_all_keys.find(p => new RegExp(area, 'ig').test(p))
50 | // 如果用户的area在r_area_all中不存在,则使用随机省份
51 | const province = curProvince ? formSpecificProvinceToShort(curProvince) : randomProvince()
52 | // 如果用户的sex不存在,则默认为男
53 | const sex = curSex && curSex !== 'unknown' ? sexBelong[curSex] : '男'
54 | const nickname = nicknameSymbols.find(s => s === curNickname.trim()) ? '入门' : curNickname
55 | return `${province}-${sex}-${nickname}`
56 | }
57 | // 获取修改后的用户的群昵称中的省份
58 | const getChangeProvinceCard: ChangeCard = async (bot, gId, userId) => {
59 | const beAtInfoGroup = await bot.getGroupMemberInfo(gId, userId)
60 | const { area, card } = beAtInfoGroup.data
61 | const r_area_all_keys = Object.keys(areaAll)
62 | const curProvince = r_area_all_keys.find(p => new RegExp(area, 'ig').test(p))
63 | // 如果用户的area在r_area_all中不存在,则使用随机省份
64 | const province = curProvince ? formSpecificProvinceToShort(curProvince) : randomProvince()
65 | const result = getSpecificCard(card)
66 | result[0] = province
67 | return result.join('-')
68 | }
69 | // 获取修改后的某位用户群昵称中的性别
70 | const getChangeSexCard: ChangeCard = async (bot, gId, userId) => {
71 | const beAtInfoGroup: BeAtInfoGroupSex = await bot.getGroupMemberInfo(gId, userId)
72 | const { sex, card } = beAtInfoGroup.data
73 | const bar = sex && sex !== 'unknown' ? sexBelong[sex] : '男'
74 | const result = getSpecificCard(card)
75 | result[1] = bar
76 | return result.join('-')
77 | }
78 | // 获取修改后的用户的群昵称中的名字
79 | const getChangeNicknameCard: ChangeCard = async (bot, gId, userId) => {
80 | const beAtInfoGroup = await bot.getGroupMemberInfo(gId, userId)
81 | const { nickname, card } = beAtInfoGroup.data
82 | const isNoKeys = keywordsReg.test(nickname) || nicknameSymbols.find(s => s === nickname.trim())
83 | const bar = isNoKeys ? '小萌新' : nickname
84 | const result = getSpecificCard(card)
85 | result[2] = bar
86 | return result.join('-')
87 | }
88 |
89 | export {
90 | isCardRule,
91 | getChangeAllCard,
92 | getChangeProvinceCard,
93 | getChangeSexCard,
94 | getChangeNicknameCard
95 | }
96 |
--------------------------------------------------------------------------------
/eventsHandle/groupchat/repeater.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [检测复读]指令:
3 | * 当群聊中发生了复读事件后,小秋会根据复读的人数及消息数作出相应回应。
4 | * (该指令会自动进行检测,因此无法主动开启/关闭。在主动进行复读时,小秋会避开复读已开发的指令文字,
5 | * 理论上来讲不会造成这种情况,因为每条指令始终都对应着一条回复)
6 | */
7 | import path from 'path'
8 |
9 | import { menu } from '../../lib/oicq/menuKeywords'
10 | import { operations } from '../../lib/oicq/oicqOperations'
11 | import { returnOneOfContent } from '../../lib/methods'
12 |
13 | import type { GroupchatsId, OriginData } from '../../lib/interface'
14 | import type { PreGroupchatMessage, CommandsName } from '../../database/forms/interface'
15 |
16 | type Repeater = (
17 | bot: OriginData['bot'],
18 | id: GroupchatsId,
19 | card: string,
20 | operations: OriginData['operations']
21 | ) => Promise
22 |
23 | const { promiseImage, face } = operations
24 |
25 | // 用于分析群聊中复读的情况
26 | const getSecondRepeatItems = (arr: PreGroupchatMessage[]) => {
27 | // 得到每个重复的消息及它们被重复的次数
28 | const container = new Map([])
29 | // 统计每个重复的消息都是由哪些用户发出的
30 | const user = new Set()
31 | const result = {
32 | // 被复读的消息
33 | msg: '',
34 | // 被复读消息的最大复读次数
35 | times: 0,
36 | // 被复读消息总共有多少人进行复读
37 | userCounts: 0
38 | }
39 | arr.forEach(({ userId, content }) => {
40 | const counts = container.get(content)
41 | // 如果该消息没有出现过,则默认出现过一次
42 | if (!counts) return container.set(content, 1)
43 | const num = counts + 1
44 | // 如果未超过当前最高的复读次数,则只更新该消息自身所对应的复读次数
45 | if (num < result.times) return container.set(content, num)
46 | // 如果超过了当前最高的复读次数,则更新result对象(因为此时统计到了最新数据)
47 | result.msg = content
48 | result.times = num
49 | container.set(content, num)
50 | // 用于统计都是哪些人复读的消息
51 | user.add(userId)
52 | })
53 | result.userCounts = user.size
54 | return result
55 | }
56 | const reason1 = [
57 | `你有孤独和烈酒,所以在这复读走一走?`,
58 | `小秋发现你很无聊哎`,
59 | `别复读了,整一局?${promiseImage(path.resolve('./assets/images/emoji/送你花花1.jpg'))}`,
60 | `别复读了,整一局?${face(98)}`,
61 | `别复读了,整一局?${promiseImage(path.resolve('./assets/images/emoji/比心1.jpg'))}`,
62 | `别复读了,整一局?${face(179)}`
63 | ]
64 | const reason2 = [
65 | `${path.resolve('./assets/images/emoji/复读1.jpg')}`,
66 | `${path.resolve('./assets/images/emoji/不要这样先生1.jpg')}`,
67 | `${path.resolve('./assets/images/emoji/复读2.jpg')}`,
68 | `${path.resolve('./assets/images/emoji/两狗对视1.jpg')}`,
69 | `${path.resolve('./assets/images/emoji/以德服人1.jpg')}`,
70 | `${path.resolve('./assets/images/emoji/帅者的肯定1.jpg')}`
71 | ]
72 |
73 | // 每次复读均有100分钟的冷却时间
74 | let isRepeat = true
75 | const repeater: Repeater = async (bot, id, card, operations) => {
76 | // 是否在冷却时间内
77 | if (!isRepeat) return
78 | // 复读功能有80%的几率触发
79 | if (Math.random() >= 0.2) return
80 | const { getHistoryGroupMsg, promiseImage } = operations
81 | // 以本群最近的10条消息为样本进行分析
82 | const latest = getHistoryGroupMsg(id, 10)
83 | const { msg, times, userCounts } = getSecondRepeatItems(latest)
84 | // 只有复读次数大于3才认定此行为的本质是复读
85 | if (times < 3) return
86 | // 如果复读的次数大于3,且复读的用户等于1,则提醒该用户不要复读(造成这种情况的原因是某位成员在独自进行复读)
87 | // 如果复读的次数大于3,且复读的用户等于2,则小秋发送一张图片,主动打断复读(造成这种情况的原因是只有两位用户在复读)
88 | // 如果复读的次数大于3,且复读的用户大于等于3,则小秋也加入复读(造成这种情况的原因是有多位用户在复读)
89 | isRepeat = false
90 | // 定时清除冷却时间(小秋检测复读的冷却时间为100分钟)
91 | setTimeout(() => {
92 | isRepeat = true
93 | }, 1000 * 60 * 60 * 100)
94 | const repeatGo = [
95 | () => bot.sendGroupMsg(id, `${card},${returnOneOfContent(reason1)}`),
96 | () => bot.sendGroupMsg(id, `${promiseImage(returnOneOfContent(reason2))}`)
97 | ]
98 | const who = repeatGo[userCounts - 1]
99 | if (who) return who()
100 | // 当全部判断通过后,此时已经可以让小秋加入复读,但若被复读消息的内容是某个已开发指令的名称,则不进行复读
101 | // 无论是否与指令名称冲突,均进入冷却时间内
102 | const isConflict = Object.keys(menu) as CommandsName[]
103 | const ok = isConflict.find(name => name === msg)
104 | if (ok) return
105 | bot.sendGroupMsg(id, msg)
106 | }
107 |
108 | export { repeater }
109 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/changeDrawDiscount.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [修改积分抽奖折扣]指令:
3 | * 用于修改积分抽奖的折扣数(5-9折)
4 | */
5 | import {
6 | getAssignTimestamp,
7 | transformTimeNameEn,
8 | getWholeDate,
9 | getSpecificTimeMS
10 | } from '../../lib/time'
11 |
12 | import { AuthId, GroupAt } from '../../lib/interface'
13 | import type { SendContent, CommandFn } from '../../lib/interface'
14 | import type { GroupsConfig } from '../../database/forms/interface'
15 | import type { UnitChi } from '../../lib/time/interface'
16 | import type { OicqOperations } from '../../lib/oicq/interface'
17 |
18 | const member = (at: GroupAt, sex: boolean, face: OicqOperations['face']) => {
19 | const male = [
20 | `${at('user')} 好哥哥 你干嘛要开启折扣呀${face(104)}`,
21 | `${at('user')} 小秋不能帮您开启哦~${face(104)}`,
22 | `${at('user')} 呜呜呜 因为不能帮哥哥开启折扣,所以小秋的心好痛${face(104)}`
23 | ]
24 | const female = [
25 | `${at('user')} 小秋只会帮我的好闺蜜开启哦~${face(104)}`,
26 | `${at('user')} 呜呜 答应做小秋的闺蜜,小秋就帮您开启哦${face(104)}`,
27 | `${at('user')} 呜呜呜 因为不能帮给小姐姐使用折扣了,所以小秋的心好痛痛${face(104)}`
28 | ]
29 | return sex ? male : female
30 | }
31 | const sendContent: SendContent = {
32 | name: '抽奖限时折扣',
33 | reg: /^积分抽奖池开启(?\d+)折,为期(?\d*)(?.*)$/,
34 | role: [AuthId.FuncJin],
35 | member: ({ user: { sex }, operations: { at, face } }) => member(at, sex, face),
36 | admin: ({ user: { sex }, operations: { at, face } }) => member(at, sex, face),
37 | owner: ({ user: { sex }, operations: { at, face } }) => member(at, sex, face),
38 | deverDefined: ({ operations: { at }, defined: { unit, dur, discount, lastTime } }) => [
39 | [
40 | `${at('user')} ${discount}折不行哦,小秋能够帮您开启的折扣数必须要处于5-9期间`,
41 | `${at('user')} 哎呀 小秋暂时只能开启5-9折,还不支持${discount}折哦~`,
42 | `${at('user')} ${discount}折不行啊,折扣数必须位于5-9之间哦~`
43 | ],
44 | [
45 | `${at('user')} 给个时间单位哦~`,
46 | `${at('user')} 听说没有准确的时间,是不能够开启积分抽奖池限时折扣的`,
47 | `${at('user')} 咦 没有具体的期限是不能开启限时折扣哦~`
48 | ],
49 | [
50 | `${at('user')} 小秋认为${unit}不是合格的时间单位哦~`,
51 | `${at('user')} 小秋暂时还不认识${unit}哎~`,
52 | `${at('user')} 不能够是${unit}哦,换个时间单位再试试吧`
53 | ],
54 | [
55 | `${at('user')} ${unit}不合法哦,换个时间再试试吧`,
56 | `${at('user')} ${unit}太小了呀,尝试换个大一点的时间`,
57 | `${at('user')} 小秋觉得${unit}不符合折扣期限,换个时间段再试试吧~`
58 | ],
59 | [
60 | `积分奖池已开启为期${dur}${unit}的限时折扣(${discount}折),快来抽奖吧~${lastTime}`,
61 | `${at('user')} 小秋已将奖池折扣改为${discount}折,并且会持续${dur}${unit}哦${lastTime}`,
62 | `哇 积分抽奖池${discount}折来袭,持续${dur}${unit},赶紧来抽奖吧!${lastTime}`,
63 | `${at('user')} 小秋已开启积分抽奖池的限时折扣,听说连续水群的抽中概率更高哦~${lastTime}`
64 | ]
65 | ]
66 | }
67 | const fn: CommandFn = async originData => {
68 | const { bot, group, raw_message, reg, database } = originData
69 | const { getDataBaseData, formSet: { groups_config } } = database
70 | const { dur, unit, discount: drawDiscount } = reg.exec(raw_message)?.groups!
71 | const discount = Number(drawDiscount) as GroupsConfig['draw']['discount']
72 | if (discount < 5 || discount > 9) return { items: 1, args: { discount } }
73 | const timeKey = transformTimeNameEn[unit as UnitChi]
74 | if (!dur) return { items: 2 }
75 | if (!timeKey) return { items: 3, args: { unit } }
76 | if (Number(dur) <= 0) return { items: 4, args: { dur } }
77 | const initTime = { mins: 0, hours: 0, days: 0 }
78 | const nextTime = { ...initTime, [timeKey]: dur }
79 | const timestamp = getAssignTimestamp(nextTime)
80 | const config = await getDataBaseData(groups_config.name, groups_config.retrieveData)(group.id)
81 | const draw = { ...config.draw, discount, timestamp }
82 | await getDataBaseData(groups_config.name, groups_config.updateData)(group.id, { ...config, draw })
83 | const lastTime = `\n[${getWholeDate(new Date(timestamp))}分会自动关闭]`
84 | setTimeout(() => {
85 | bot.sendGroupMsg(group.id, '小秋已关闭积分抽奖池限时折扣')
86 | }, getSpecificTimeMS(nextTime))
87 | return { items: 5, args: { unit, dur, lastTime, discount } }
88 | }
89 | const changeDrawDiscount = { fn, sendContent }
90 |
91 | export { changeDrawDiscount }
92 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/propCard/banAndDelCard.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [禁言卡]、[撤回卡]指令:
3 | * 禁言卡会自动禁言指定成员5分钟,撤回卡会自动撤回指定成员的1条消息
4 | */
5 | import { setPacksack } from '../packsack/tools'
6 |
7 | import type { SendContent, CommandFn } from '../../../lib/interface'
8 |
9 | const sendContent: SendContent = {
10 | name: '道具卡',
11 | reg: /^(?(禁言)|(撤回))卡\[CQ:at,qq=(?\d*),text=.*\]\s*$/,
12 | role: 'member',
13 | member: ({ user: { name }, operations: { at } }) => [
14 | `小小${name} 可笑可笑,您的背包为空,所以小秋不能帮您使用此道具哦~`,
15 | `${name},您背包目前为空,暂时无法使用,赶快去获取吧~`,
16 | `${at('user')} 您的背包中尚不存在此道具哦,因此无法使用`
17 | ],
18 | admin: ({ user: { name }, operations: { at } }) => [
19 | `小小管理 ${name},可笑可笑,背包为空,所以小秋不能帮您使用此道具哦~`,
20 | `${name},您还没有此道具卡,赶快去获取吧~`,
21 | `${at('user')} 管理大哥 你背包里还没有道具卡呢,因此小秋无法使用`
22 | ],
23 | owner: ({ user: { name }, operations: { at } }) => [
24 | `小小群主 ${name},可笑可笑,背包为空,所以不能使用!!`,
25 | `${name},您还没有此道具卡哎,赶快去获取吧~`,
26 | `${at('user')} 我说群主啊 你背包里还没有道具卡呢,小秋不能帮您使用`
27 | ],
28 | deverDefined: ({ user: { name: username }, other: { name: othername }, operations: { at } }) => [
29 | [
30 | `${at('user')} 小秋发现您的背包中不存在禁言卡,所以无法使用`,
31 | `${at('user')} 不要妄图欺骗小秋哦 因为您背包中不存在禁言卡,所以无法使用`,
32 | `${username},你还没有禁言卡呢,赶快去获取吧~`
33 | ],
34 | [
35 | `${at('user')} 很不幸,对方<${at('other')}>自动消耗一张免疫卡,免除了此次禁言`,
36 | `${at('user')} 唉 真不凑巧 <${at('other')}>自动消耗一张免疫卡,竟然免除了此次禁言`,
37 | `${at('user')} 芭比Q啦,<${at('other')}>自动消耗一张免疫卡,免除了此次禁言`
38 | ],
39 | [
40 | `${at('user')} 小秋发现您的背包中还没有撤回卡哦,所以无法使用~`,
41 | `${at('user')} 小秋看到您的撤回卡数量为0,所以无法使用~`,
42 | `${username},你还没有撤回卡呢,赶快去获取吧~`
43 | ],
44 | [
45 | `${at('user')} 灰常抱歉,<${at('other')}>自动消耗一张免疫卡,免除了此次撤回`,
46 | `${at('user')} <${at('other')}>自动消耗一张免疫卡,免除了此次撤回`,
47 | `${at('user')} 芭比Q啦,<${at('other')}>自动消耗一张免疫卡,免除了此次撤回`
48 | ],
49 | [
50 | `${at('user')} 小秋没有看到${othername}发言哦~\n[撤回卡已自动消耗]`,
51 | `${at('user')} 咦 是我眼花了吗?好像${othername}没有发言哦~\n[撤回卡已自动消耗]`,
52 | `${at('user')} 小秋检测到${othername}没有发言啊\n[撤回卡已自动消耗]`
53 | ]
54 | ],
55 | equal: ({ other: { name, role }, operations: { at } }) => [
56 | `${at('user')} 先不扣你的卡了 下次不准向${name}使用了`,
57 | `${at('user')} 飘了是吧 还敢对${name}用道具卡??`,
58 | `${at('user')} 再向${role === 'admin' ? '管理' : '群主'}使用道具卡 小心我干你`
59 | ],
60 | level: () => [
61 | `对不起,小秋权限不足,您暂时无法发挥此卡的最大价值\n[暂未扣除此道具卡数量]`,
62 | `呜呜呜 就连小秋都好难过,使用道具卡失败,因为小秋无此权限~\n[暂未扣除此道具卡数量]`,
63 | `哭了 小秋竟然没有权限去帮您使用此卡\n[暂未扣除此道具卡数量]`
64 | ]
65 | }
66 | const fn: CommandFn = async originData => {
67 | const { group, user, other, raw_message, operations, reg, database } = originData
68 | const { getDataBaseData, formSet: { user_packsack } } = database
69 | const { delMsg, banMember } = operations
70 | const value = await getDataBaseData(user_packsack.name, user_packsack.retrieveData)(group.id)
71 | const curUser = value[user.id]
72 | if (!curUser) return
73 | const { card } = curUser
74 | const type = reg.exec(raw_message)?.groups?.type
75 | const allOtherCard = value[other.id]?.card
76 | if (type === '禁言') {
77 | if (!card.ban) return { items: 1 }
78 | setPacksack(group.id, user.id, { card: { ban: -1 } })
79 | if (allOtherCard?.immune) {
80 | setPacksack(group.id, other.id, { card: { immune: -1 } })
81 | setPacksack(group.id, user.id, { card: { ban: -1 } })
82 | return { items: 2 }
83 | }
84 | // 对方无免疫卡,且自己拥有禁言卡时,视为使用成功
85 | banMember(group.id, other.id, '分钟', 5)
86 | return { noMsg: true }
87 | }
88 | if (!card.delMsg) return { items: 3 }
89 | setPacksack(group.id, user.id, { card: { delMsg: -1 } })
90 | if (allOtherCard?.immune) {
91 | setPacksack(group.id, other.id, { card: { immune: -1 } })
92 | setPacksack(group.id, user.id, { card: { delMsg: -1 } })
93 | return { items: 4 }
94 | }
95 | // 对方无免疫卡,且自己拥有撤回卡时,视为使用成功
96 | const count = await delMsg(group.id, other.id, 1)
97 | if (!count) return { items: 5 }
98 | return { noMsg: true }
99 | }
100 | const banAndDelCard = { fn, sendContent }
101 |
102 | export { banAndDelCard }
103 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/draw/tools.ts:
--------------------------------------------------------------------------------
1 | import { getDataBaseData, formSet } from '../../../database'
2 | import { returnOneOfContent } from '../../../lib/methods'
3 |
4 | import type { CardResult, AllResult } from './interface'
5 | import type { DataBase } from '../../../database/interface'
6 | import type { GroupchatsId } from '../../../lib/interface'
7 |
8 | type Judge = (
9 | group_id: GroupchatsId,
10 | user_id: number,
11 | count: number,
12 | database: DataBase,
13 | drawPlaceholder: Map
14 | ) => Promise<{
15 | flag: boolean
16 | reason: string
17 | }>
18 |
19 | const { groups_config } = formSet
20 |
21 | // 判断是否处于限时折扣期间
22 | const isDiscount = async (gId: GroupchatsId) => {
23 | const { draw: { discount, timestamp } } = await getDataBaseData(groups_config.name, groups_config.retrieveData)(gId)
24 | const curTime = +new Date()
25 | if (curTime < timestamp) return { flag: true, discount, timestamp }
26 | return { flag: false }
27 | }
28 | // 得到在某个群聊中进行一次抽奖所消耗的积分
29 | const getSinglePrice = async (gId: GroupchatsId) => {
30 | const { flag, discount } = await isDiscount(gId)
31 | const price = flag ? (discount! / 10) * 6 : 6
32 | return price
33 | }
34 | const reason1 = (count: number) => {
35 | const reason = [
36 | `你刚才的${count}次抽奖还没抽完呢,再等会`,
37 | `哎呀 小秋发现你刚才的${count}次抽奖还没完成哦`,
38 | `小秋还在努力的将您刚才的${count}次抽奖抽完,请稍等一会哦~`
39 | ]
40 | return returnOneOfContent(reason)
41 | }
42 | const reason2 = () => {
43 | const reason = [
44 | `小秋发现您的背包为空,暂时无法进行抽奖`,
45 | `呜呜 您的背包中没有积分,因此小秋不能帮您进行抽奖`,
46 | `小秋的心好痛,因为您背包中没有积分可以进行抽奖`
47 | ]
48 | return returnOneOfContent(reason)
49 | }
50 | const reason3 = (text: string | number) => {
51 | const reason = [
52 | `小秋发现您的积分不足以完成${text}次抽奖哦~`,
53 | `小秋检测到您的积分不足以完成${text}次抽奖`,
54 | `您不足以完成${text}次抽奖,因为小秋发现您背包中的积分不足`
55 | ]
56 | return returnOneOfContent(reason)
57 | }
58 | const judge: Judge = async (group_id, user_id, count, database, drawPlaceholder) => {
59 | const { getDataBaseData, formSet: { user_packsack } } = database
60 | // 先判断当前用户的上一次抽奖是否完成,如果未完成,则返回true
61 | const drawFlag = `${group_id}${user_id}`
62 | if (drawPlaceholder.has(drawFlag))
63 | return { flag: false, reason: reason1(drawPlaceholder.get(drawFlag)!) }
64 | const value = await getDataBaseData(user_packsack.name, user_packsack.retrieveData)(group_id)
65 | const curUser = value[user_id]
66 | if (!curUser) return { flag: false, reason: reason2() }
67 | const text = count === 1 ? '此' : count
68 | const price = await getSinglePrice(group_id)
69 | if (Number(curUser.score) < count * price) return { flag: false, reason: reason3(text) }
70 | return { flag: true, reason: `可以抽奖` }
71 | }
72 | const luckly = async (gId: GroupchatsId) => {
73 | const property = [
74 | { name: '禁言卡', code: 'ban' },
75 | { name: '未抽中', code: 'no' },
76 | { name: '免疫卡', code: 'immune' },
77 | { name: '4积分', code: 'score' },
78 | { name: '撤回卡', code: 'delMsg' },
79 | { name: '康康卡', code: 'see' }
80 | ]
81 | const Prab = [0.1, 1, 0.2, 0.8, 0.2, 0.7]
82 | //const Prab = [0.8, 1, 0.8, 1, 0.8, 0.6]
83 | const Alias = [2, 2, 4, 4, 2, 4]
84 | const advance = {
85 | prop: {
86 | name: '',
87 | code: 'no'
88 | },
89 | explain: ''
90 | }
91 | const randomFirst = Math.round(Math.random() * 5)
92 | const randomSecode = Math.random()
93 | advance.prop = randomSecode < Prab[randomFirst] ? property[randomFirst] : property[Alias[randomFirst] - 1]
94 | advance.explain = advance.prop.code === 'no' ? '未抽中~' : `${advance.prop.name}`
95 | const all: AllResult = {
96 | score: 0,
97 | card: {
98 | ban: 0,
99 | immune: 0,
100 | delMsg: 0,
101 | see: 0
102 | }
103 | }
104 | // 单次抽奖所需要的积分
105 | const price = await getSinglePrice(gId)
106 | const score = Number(price.toFixed(1)) * -1
107 | if (advance.prop.code === 'no') {
108 | all.score = score
109 | } else if (advance.prop.code === 'score') {
110 | all.score = score + 4
111 | } else {
112 | all.score = score
113 | all.card = { [advance.prop.code]: 1 } as CardResult
114 | }
115 | return { all, advance }
116 | }
117 |
118 | export {
119 | judge,
120 | luckly,
121 | isDiscount
122 | }
123 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/draw/drawCount.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [抽奖xx]指令:
3 | * 用于在积分抽奖池中进行多次抽奖
4 | */
5 | import { judge, luckly } from './tools'
6 | import { self } from '../../../lib/user'
7 | import { setPacksack } from '../packsack/tools'
8 | import { returnOneOfContent, formatNumCount, isDecimals } from '../../../lib/methods'
9 |
10 | import type { CardResult, LucklyResult } from './interface'
11 | import type { SendContent, CommandFn, ForwardRecord } from '../../../lib/interface'
12 |
13 | type DrawResult = {
14 | content: string
15 | arr: LucklyResult[]
16 | }
17 |
18 | // 每位用户只能在上一次抽奖完成后,再进行下一次抽奖
19 | const drawPlaceholder = new Map()
20 |
21 | const sendContent: SendContent = {
22 | name: '抽奖',
23 | reg: [/^抽奖x(?\d+)$/, /^抽奖\*(?\d+)$/, /^抽奖(?\d+)次$/],
24 | role: 'member',
25 | member: ({ user: { name }, operations: { at } }) => [
26 | `${at('user')} 你身为一个群成员,你不知道抽奖次数最低为1吗?`,
27 | `小秋好伤心啊 因为${name}不知道最低抽奖次数是1次`,
28 | `${at('user')} 难道你要成为群主才知道抽奖次数最低是1次吗?`
29 | ],
30 | admin: ({ user: { name }, operations: { at } }) => [
31 | `${name}!你这管理别要了,最低抽奖次数只能是1次`,
32 | `${at('user')} 呜呜 这位管理,小秋好难过,因为您最少要进行的抽奖次数为1哦`,
33 | `管理员${name}真抠啊,连小秋都知道最低抽奖次数是1次`
34 | ],
35 | owner: ({ user: { name }, operations: { at } }) => [
36 | `群主别搞事,最低抽奖次数是1次`,
37 | `${name} 你咋回事,最低抽奖次数是1次!小秋可不怕你这个坏群主哦`,
38 | `${at('user')} 别搞事啊群主 最低的抽奖次数可是1次呀`
39 | ],
40 | deverDefined: ({ user: { name }, operations: { at }, defined: { reason, explain } }) => [
41 | [
42 | `${at('user')} ${reason}`
43 | ],
44 | [
45 | `${at('user')} 抽到这么多东西,小秋好羡慕啊。${explain}\n`,
46 | `吆西 ${name}人品大爆发,${explain}\n`,
47 | `${at('user')} 运气不错,羡慕你哦\n${explain}\n`
48 | ]
49 | ]
50 | }
51 | const fn: CommandFn = async originData => {
52 | const { bot, group, user, raw_message, segment, reg, database } = originData
53 | const count = Number(reg.exec(raw_message)?.groups?.count)
54 | if (count <= 0) return
55 | const isOk = await judge(group.id, user.id, count, database, drawPlaceholder)
56 | const { flag, reason } = isOk
57 | if (!flag) return { items: 1, args: { reason } }
58 | // 记录本次抽奖是否完成
59 | const drawFlag = `${group.id}${user.id}`
60 | drawPlaceholder.set(drawFlag, count)
61 | const result: DrawResult = { content: '', arr: [] }
62 | const process = async () => await luckly(group.id)
63 | for (let i = 0; i < count; i++) result.arr.push(await process())
64 | const end = { score: 0, card: { ban: 0, delMsg: 0, immune: 0, see: 0 } }
65 | result.arr.forEach((v, i) => {
66 | const { all, advance } = v
67 | end.score += all.score
68 | const arr = Object.keys(all.card) as (keyof CardResult)[]
69 | arr.forEach(v => (end.card[v] += all.card[v]))
70 | result.content += `${i + 1}:${advance.explain}\n`
71 | })
72 | end.score = Number(end.score.toFixed(1))
73 | await setPacksack(group.id, user.id, end)
74 | // 确认更改数据后,清除原先所记录的抽奖标识
75 | drawPlaceholder.delete(drawFlag)
76 | const r = end.score
77 | const text = (content: string) =>
78 | `抽奖记录为:\n${content}[本次抽奖积分变化:${formatNumCount(isDecimals(r) ? Number(r.toFixed(1)) : r)}积分]`
79 | // 抽奖次数超过10时,以转发聊天记录的形式发出
80 | if (count <= 10) return { items: 2, args: { explain: text(result.content) } }
81 | const arr = [
82 | `大家快看 ${user.name}居然抽到了这么多东西`,
83 | `羡慕啊 小机灵鬼${user.name} 这么多次抽奖居然没亏哎`,
84 | `hhh ${user.name},快看看你都抽到了啥吧~`
85 | ]
86 | // 由于qq对每条消息的字数存在限制,所以此处以2000个字符为例,当超出此限制,则在聊天记录中对抽奖结果进行分割
87 | const rowMaxLength = 2000
88 | const drawCountResult: ForwardRecord[] = []
89 | const getFormat = (message: string) => ({
90 | user_id: self.uin,
91 | nickname: '抽奖结果',
92 | message
93 | })
94 | if (result.content.length <= rowMaxLength) {
95 | drawCountResult.push(getFormat(`@${user.card},${text(result.content)}`))
96 | } else {
97 | const rule = Math.floor(result.content.length / rowMaxLength)
98 | Array.from({ length: rule }).forEach((v, i) => {
99 | const start = i * rowMaxLength
100 | const end = (i + 1) * rowMaxLength
101 | const boundary = i === rule ? result.content.length : end
102 | const text = result.content.slice(start, boundary)
103 | drawCountResult.push(getFormat(`【第${i + 1}条消息记录】\n\n${text}`))
104 | })
105 | }
106 | const forward = await bot.makeForwardMsg(drawCountResult)
107 | bot.sendGroupMsg(group.id, returnOneOfContent(arr))
108 | bot.sendGroupMsg(group.id, segment.xml(forward.data.data.data))
109 | return { noMsg: true }
110 | }
111 | const drawCount = { fn, sendContent }
112 |
113 | export { drawCount }
114 |
--------------------------------------------------------------------------------
/eventsHandle/scorePlayMethods/propCard/seeCard.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [康康卡]指令:
3 | * 使用该道具卡会自动消耗当前成员2积分,表现形式为随机发送一张图片
4 | * (api:风景图、头像图、二次元)
5 | */
6 | import fs from 'fs'
7 | import path from 'path'
8 | import request_r from 'request'
9 |
10 | import { getRandom } from '../../../lib/methods'
11 | import { setPacksack } from '../packsack/tools'
12 |
13 | import type { SendContent, CommandFn } from '../../../lib/interface'
14 | import type { NoParamCallback } from 'fs'
15 |
16 | type SeeCard = {
17 | title: string
18 | msg: string
19 | }
20 |
21 | const request = request_r.defaults({ encoding: null })
22 |
23 | const writeFileFromRequest = (https: string, localUrl: string, fn: NoParamCallback) =>
24 | request(https, (_: unknown, __: unknown, data: string) => fs.writeFile(`${localUrl}`, data, fn))
25 |
26 | const sendContent: SendContent = {
27 | name: '道具卡',
28 | reg: /^康康卡$/,
29 | role: 'member',
30 | member: ({ user: { name }, operations: { at } }) => [
31 | `小小${name} 可笑可笑,您的背包为空,所以小秋不能帮您使用此道具哦~`,
32 | `${name},您背包目前为空,暂时无法使用,赶快去获取吧~`,
33 | `${at('user')} 您的背包中尚不存在此道具哦,因此无法使用`
34 | ],
35 | admin: ({ user: { name }, operations: { at } }) => [
36 | `小小管理 ${name},可笑可笑,背包为空,所以小秋不能帮您使用此道具哦~`,
37 | `${name},您还没有此道具卡,赶快去获取吧~`,
38 | `${at('user')} 管理大哥 你背包里还没有道具卡呢,小秋不能帮您使用`
39 | ],
40 | owner: ({ user: { name }, operations: { at } }) => [
41 | `小小群主 ${name},可笑可笑,背包为空,所以不能使用!!`,
42 | `${name},您还没有此道具卡哎,赶快去获取吧~`,
43 | `${at('user')} 我说群主啊 你背包里还没有道具卡呢,小秋不能帮您使用`
44 | ],
45 | deverDefined: ({ operations: { at }, defined }) => [
46 | [
47 | `${at('user')} 嗯?就这么想康好康的吗,但是你没有康康卡!!!`,
48 | `${at('user')} 小秋发现您还没有康康卡哦~`,
49 | `${at('user')} 没有康康卡的选手,是不可以康好看的哦~`
50 | ],
51 | [
52 | `${at('user')} 人品大爆发!您使用一张[康康卡]发现了一张${defined.title}${defined.content}`,
53 | `${at('user')} 哇 小秋为您找到了一张${defined.title}${defined.content}`,
54 | `${at('user')} 芜湖 您居然康到了一张绝美的${defined.title}${defined.content}`
55 | ]
56 | ]
57 | }
58 | const fn: CommandFn = async originData => {
59 | const { group, user, operations, database } = originData
60 | const { getDataBaseData, formSet: { user_packsack } } = database
61 | const value = await getDataBaseData(user_packsack.name, user_packsack.retrieveData)(group.id)
62 | const curUser = value[user.id]
63 | if (!curUser) return
64 | const { card } = curUser
65 | if (card.see <= 0) return { items: 1, args: {} }
66 | const randomType = getRandom(0, 2)
67 | const type = [
68 | ['https://img.xjh.me/random_img.php', '二刺猿图'],
69 | ['https://api.ixiaowai.cn/gqapi/gqapi.php', '风景图'],
70 | ['https://api.sunweihu.com/api/sjtx/api.php?lx=', '头像图']
71 | ]
72 | const backErCiYuan = (curTypeRandom: string[]): Promise =>
73 | new Promise(r => {
74 | const [https, title] = curTypeRandom
75 | request(https, (_: unknown, __: unknown, data: string) => {
76 | const name = /img.xjh.me\/img\/(?.*)\.jpg" src/.exec(data)?.groups?.name
77 | const seeCard_img_local_url = path.resolve(`./assets/images/seeCard/${name}.jpg`)
78 | const https = `https://img.xjh.me/img/${name}.jpg`
79 | writeFileFromRequest(https, seeCard_img_local_url, () =>
80 | r({ title, msg: seeCard_img_local_url })
81 | )
82 | })
83 | })
84 | const backScenery = (curTypeRandom: string[]): Promise =>
85 | new Promise(r => {
86 | const [https, title] = curTypeRandom
87 | const random = getRandom(1, 999)
88 | const seeCard_img_local_url = path.resolve(`./assets/images/seeCard/scenery${random}.jpg`)
89 | writeFileFromRequest(https, seeCard_img_local_url, () =>
90 | r({ title, msg: seeCard_img_local_url })
91 | )
92 | })
93 | const backAvator = (curTypeRandom: string[]): Promise =>
94 | new Promise(r => {
95 | // [参数:lx] [ 值:男头a1, 女头b1, 动漫c1, 动漫女头c2, 动漫男头c3 ]
96 | const [https, title] = curTypeRandom
97 | const avatorType = [
98 | ['a1', '男头'],
99 | ['b1', '女头'],
100 | ['c1', '动漫'],
101 | ['c2', '动漫女头'],
102 | ['c3', '动漫男头']
103 | ]
104 | const [types, text] = avatorType[getRandom(0, 4)]
105 | const random = getRandom(1, 999)
106 | const seeCard_img_local_url = path.resolve(`./assets/images/seeCard/avator${random}.jpg`)
107 | writeFileFromRequest(`${https}${types}`, seeCard_img_local_url, () =>
108 | r({ title: `${title}-${text}`, msg: seeCard_img_local_url })
109 | )
110 | })
111 | const get = [backErCiYuan, backScenery, backAvator]
112 | const data = await get[randomType](type[randomType])
113 | const { title, msg } = data
114 | setPacksack(group.id, user.id, { card: { see: -1 } })
115 | return { items: 2, args: { title, content: operations.promiseImage(msg) } }
116 | }
117 | const seeCard = { fn, sendContent }
118 |
119 | export { seeCard }
120 |
--------------------------------------------------------------------------------
/eventsHandle/groupchatAdmin/checkDeadPerson/checkDeadPerson.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [人机验证]指令:
3 | * 使用该指令可以对某一成员进行“人机验证”,形式为向该成员发送一道随机题目;
4 | * 若成员在规定时间内回答正确,则视为人机验证通过,未通过人机验证将被踢出群聊
5 | */
6 | import path from 'path'
7 |
8 | import { returnOneOfContent } from '../../../lib/methods'
9 | import { persons, inCheckDeadOf } from './tools'
10 |
11 | import type { TopicInfo } from './interface'
12 | import type { SendContent, CommandFn, GroupchatsId } from '../../../lib/interface'
13 |
14 | type TempReturn = [string, TopicInfo]
15 |
16 | const verifyTip = '若三分钟内未作答,或三分钟内累计答错3次,将被踢出群聊'
17 |
18 | const sendContent: SendContent = {
19 | name: '人机验证',
20 | reg: [
21 | /^\s*人机验证\[CQ:at,qq=(?\d*),text=.*\]\s*$/,
22 | /^\s*开启人机验证\[CQ:at,qq=(?\d*),text=.*\]\s*$/,
23 | /^\s*使用人机验证\[CQ:at,qq=(?\d*),text=.*\]\s*$/
24 | ],
25 | role: 'admin',
26 | member: ({ user: { name: username }, other: { name: othername }, operations: { at, face, promiseImage } }) => [
27 | `${at('user')} 这???您的权限不足以开启[人机验证]指令`,
28 | `${username},抱歉 小秋检测到您暂未拥有[人机验证]指令的权限${face(98)}`,
29 | `${at('user')} 要造反了吗?这[人机验证]可不是说用就用的啊${promiseImage(path.resolve('./assets/images/emoji/反了你了1.jpg'))}`,
30 | `${username},小秋不能帮您开启人机验证`,
31 | `${at('user')} 这...小心${othername}干你啊${face(104)}`
32 | ],
33 | deverDefined: ({ user: { name: username }, other: { name: othername }, defined: { topic }, operations: { at, face, promiseImage } }) => [
34 | [
35 | `${username},${othername}当前正处于人机验证当中,请不要再次发起哦~`,
36 | `${at('user')} 小秋检测到${othername}正处于人机验证环节当中,不允许再次发起哦~`,
37 | `${at('user')} 不可以为${othername}同时进行两次人机验证哦~`,
38 | `${at('user')} 不要试图为${othername}开启两次人机验证哦${face(27)}`,
39 | `${username},请耐心等待当前人机验证完成后再开启下一次验证`,
40 | `${at('other')} ${username}居然要为你开启两次人机验证,快干他${promiseImage(path.resolve('./assets/images/emoji/以德服人1.jpg'))}`
41 | ],
42 | [
43 | `${at('other')} ${username}现对您进行人机验证。题目为:\n\n${topic}\n\n[${verifyTip}]`,
44 | `${at('other')} ${username}通知小秋对您进行人机验证。题目为:\n\n${topic}\n\n[${verifyTip}]`,
45 | `${at('other')} 小秋现在对您进行人机验证。题目为:\n\n${topic}\n\n[${verifyTip}]`,
46 | `${at('other')} 快来完成这个人机验证吧,避免被误踢哦~\n\n${topic}\n\n[${verifyTip}]`,
47 | `${at('other')} 小秋对您发起了人机验证\n\n${topic}\n\n[${verifyTip}]`,
48 | `${at('other')} 听说小秋这次准备的人机验证题目很有难度?快来完成吧~\n\n${topic}\n\n[${verifyTip}]`
49 | ]
50 | ],
51 | equal: ({ other: { name }, operations: { at } }) => [
52 | `${at('user')} 怎么个意思?验证我同行???`,
53 | `${at('user')} 哎呀 小秋还没迷糊呢 不会对${name}进行人机验证哦`,
54 | `${at('user')} 小秋不能够对${name}进行人机验证哦`,
55 | `${at('user')} 相煎何太急!!!不可以对${name}进行人机验证`,
56 | `${at('user')} 听${name}说,这人机验证可真谢谢你~`,
57 | `${at('user')} 请不要让小秋对${name}进行人机验证`
58 | ],
59 | level: ({ other: { name }, operations: { at } }) => [
60 | `${at('user')} 抱歉,小秋暂时无法使用[人机验证]指令`,
61 | `${at('user')} 哎呀 小秋好像暂时不能使用[人机验证]指令哦~`,
62 | `${at('user')} 很不幸,小秋无法为${name}开启人机验证`
63 | ]
64 | }
65 | // 人机验证的题目
66 | const verifyTopic = [
67 | ['《三国演义》的作者是谁?', ['罗贯中']],
68 | ['在JavaScript中,通过什么关键字来声明一个常量?', ['const', 'const关键字', '关键字const']],
69 | ['万有引力是谁提出来的?', ['牛顿', '英国人牛顿', '英国牛顿', '物理学家牛顿', '物理家牛顿', '英国物理学家牛顿', '英国物理家牛顿', '数学家牛顿']],
70 | ['农夫山泉矿泉水的建议零售价为几元?', ['2元', '2', '两元']],
71 | ['驾驶机动车在高速公路或者城市快速路上行驶时,驾驶人未按规定系安全带的,请问一次扣几分', ['2分', '2', '两分']],
72 | ['在markdown中,使用什么符号来定义一级标题?', ['#', '#号']],
73 | ['香港回归是在哪一年?', ['1997', '1997年', '1997年7月1日', '1997-07-01', '1997.07.01']],
74 | ['除了唱跳rap,还应该掌握哪些必备技能?', ['篮球']],
75 | ['列举一种常见的机械键盘轴体', ['红轴', '黑轴', '青轴', '茶轴', '白轴']],
76 | ['诗句"风萧萧兮易水寒"的下一句是什么?', ['壮士一去兮不复还', '壮士一去兮不复还!']]
77 | ]
78 | // 一个[人机验证]的记录形式
79 | const temp = (gId: GroupchatsId, oId: number): TempReturn => {
80 | // map: '群ID+用户ID', { topic, answer }
81 | const id = String(gId) + String(oId)
82 | // 随机抽取一道题目
83 | const [topic, answer]: any = returnOneOfContent(verifyTopic)
84 | return [id, { topic, answer, times: 0, isFailed: false }]
85 | }
86 | const fn: CommandFn = originData => {
87 | const { bot, group, other } = originData
88 | // 判断是否已经处于人机验证当中
89 | const haved = inCheckDeadOf(group.id, other.id)
90 | if (haved) return { items: 1 }
91 | const [id, oppositeQuestion] = temp(group.id, other.id)
92 | const token = setTimeout(() => {
93 | const have = persons.get(id)
94 | // 如果当前成员没有在人机验证序列中被删除,则意味着验证失败,所以将会在15秒之后被踢出本群聊
95 | // 反之,如果被删除了,则代表验证成功,所以不作出任何操作
96 | if (!have) return
97 | bot.sendGroupMsg(group.id, `由于<${other.card}>在三分钟内未能完成本次人机验证,因此将在15秒后被踢出此群聊`)
98 | setTimeout(() => {
99 | const id = String(group.id) + String(other.id)
100 | const have = persons.get(id)
101 | if (!have) return
102 | persons.delete(id)
103 | bot.setGroupKick(group.id, other.id)
104 | }, 1000 * 15)
105 | }, 1000 * 60 * 1)
106 | // 写入persons中进行记录
107 | persons.set(id, { ...oppositeQuestion, token })
108 | return { items: 2, args: { topic: oppositeQuestion.topic } }
109 | }
110 | const checkDeadPerson = { fn, sendContent }
111 |
112 | export { checkDeadPerson }
113 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | 1652360262772
74 |
75 |
76 | 1652360262772
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/search/search.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [搜掘金]指令:
3 | * 用户指定好要搜索的关键词后,小秋会依据指定好的关键词去掘金平台中爬取对应文章
4 | * (搜索到的文章篇数可选,默认为1篇)
5 | */
6 | import path from 'path'
7 | import superagent from 'superagent'
8 |
9 | import { self } from '../../../lib/user'
10 | import { getRandomDigitNumberStr } from '../../../lib/methods'
11 |
12 | import type { Articles, JueJinSearch, ArticleTemp } from './interface'
13 | import type { SendContent, CommandFn, ForwardRecord } from '../../../lib/interface'
14 |
15 | const sendContent: SendContent = {
16 | name: '搜掘金',
17 | reg: /^(搜|看|逛)(|(?\d+条))掘金(?.*)$/,
18 | role: 'member',
19 | deverDefined: ({ user: { name }, defined: { article, word }, operations: { at, face, promiseImage } }) => [
20 | [
21 | `${at('user')} 请输入一个关键词`,
22 | `${at('user')} 由于您未输入关键词,因此无法搜索哦`,
23 | `${at('user')} 小秋没有检测到文章关键词哦~${face(280)}`
24 | ],
25 | [
26 | `${at('user')} 最少搜索1条${face(278)}`,
27 | `${at('user')} 小秋认为最少要搜索1条哦~`,
28 | `${name},搜索的条数应大于等于1条哦`
29 | ],
30 | [
31 | `${at('user')} 最多只能搜索5条${face(287)}`,
32 | `${at('user')} 哎呀 条数太多了 请减少条数后重新尝试`,
33 | `${name},最多只能搜索5条~`
34 | ],
35 | [
36 | `${at('user')} 网络开小差了 请稍后再试`,
37 | `${at('user')} 本次搜索好像出了点小问题~`,
38 | `${at('user')} 小秋此次未能成功搜索,请稍后再试`
39 | ],
40 | [
41 | `${at('user')} 为您找到了有关“${word}”的内容,点击链接即可阅读~\n\n${article}`,
42 | `${at('user')} 已为您准备好了有关“${word}”的内容,点击链接即可阅读~\n\n${article}`,
43 | `${name},点击链接即可阅读有关“${word}”的内容\n${article}`
44 | ],
45 | [
46 | `${at('user')} 为您找到以下“${word}”的文章哦~`,
47 | `${at('user')} 关于“${word}”的文章好多哦~`,
48 | `${at('user')} 小秋还是第一次见这么多关于“${word}”的文章呢${promiseImage(path.resolve('./assets/images/emoji/我的人生完了1.jpg'))}`
49 | ]
50 | ]
51 | }
52 | // 发送的文章模板
53 | const temp: ArticleTemp = (id, title, abstract, author, viewCounts, likeCounts, commentCounts) => {
54 | const url = `https://juejin.cn/post/${id}`
55 | // 摘要最多40字
56 | const temp = `『标题』:${title}\n『摘要』:\n${abstract.slice(0, 35)}......\n\n作者:${author},阅读数:${viewCounts}\n点赞数:${likeCounts},评论数:${commentCounts}\n${url}`
57 | return temp
58 | }
59 | // 获取掘金文章(counts代表要获取几条)
60 | const getArticle = (keyWord: string, counts: number): Promise =>
61 | new Promise(resolve => {
62 | const uuid = getRandomDigitNumberStr(19)
63 | const reqUrl = `https://api.juejin.cn/search_api/v1/search?aid=2608&uuid=${uuid}`
64 | const body = {
65 | uuid,
66 | // 获取的推荐文章条数,最小为10
67 | limit: 10,
68 | key_word: keyWord
69 | }
70 | superagent
71 | .post(reqUrl)
72 | .send(body)
73 | .set('X-Agent', 'Juejin/Web') // 设置请求头
74 | .end((err: null | Error, { text }) => {
75 | // 请求失败
76 | if (err) return resolve({ flag: false, article: [] })
77 | const result = JSON.parse(text)
78 | const { data, err_no }: JueJinSearch = result
79 | // 请求失败
80 | if (err_no > 0) return resolve({ flag: false, article: [] })
81 | // 是否有数据
82 | if (!data.length) return resolve({ flag: false, article: [] })
83 | const article: Articles['article'] = []
84 | for (let i = 0; i < counts; i++) {
85 | const { article_info, author_user_info } = data[i].result_model
86 | const { user_name } = author_user_info
87 | const {
88 | title,
89 | article_id,
90 | brief_content,
91 | view_count,
92 | digg_count,
93 | comment_count
94 | } = article_info
95 | const content = temp(
96 | article_id,
97 | title,
98 | brief_content,
99 | user_name,
100 | view_count,
101 | digg_count,
102 | comment_count
103 | )
104 | article.push(content)
105 | }
106 | resolve({ flag: true, article })
107 | })
108 | })
109 | const fn: CommandFn = async originData => {
110 | const { bot, group, segment, raw_message, reg } = originData
111 | const { counts, word } = reg.exec(raw_message)?.groups!
112 | // 未输入关键字时
113 | if (word?.length <= 0) return { items: 1 }
114 | // 消息条数
115 | const num = Number(counts?.replace('条', ''))
116 | // 如果搜索的是0条
117 | if (num === 0) return { items: 2 }
118 | // 最多只能搜索5条
119 | if (num > 5) return { items: 3 }
120 | const impose = num ? num : 1
121 | const { flag, article } = await getArticle(word, impose)
122 | // 请求失败
123 | if (!flag) return { items: 4 }
124 | // 如果是单条,则直接发送消息
125 | if (impose === 1) return { items: 5, args: { article: article[0], word } }
126 | // 如果是多条,则以转发聊天记录的形式发出
127 | const result: ForwardRecord[] = []
128 | article.forEach(message => result.push({
129 | user_id: self.uin,
130 | nickname: '搜索文章结果',
131 | message: message
132 | }))
133 | const forward = await bot.makeForwardMsg([...result])
134 | bot.sendGroupMsg(group.id, segment.xml(forward.data.data.data))
135 | return { items: 6, args: { word } }
136 | }
137 | const searchJueJin = { fn, sendContent }
138 |
139 | export { searchJueJin }
140 |
--------------------------------------------------------------------------------
/lib/oicq/registerEvents.ts:
--------------------------------------------------------------------------------
1 | import { addCacheMsgRows } from './tools'
2 | import { getChangeAllCard } from '../../eventsHandle/groupchatAdmin/setCard/tools'
3 | import { handleMessage } from '../../eventsHandle'
4 | import { bot, segment, operations } from './oicqOperations'
5 | import { secToFormat } from '../time'
6 | import { getDataBaseData, formSet } from '../../database'
7 | import { nicknameFormCard } from '../groupchat'
8 | import { returnOneOfContent } from '../methods'
9 |
10 | import type { PreGroupchatMessage, GroupEvents } from '../../database/forms/interface'
11 | import type {
12 | OriginGroupData,
13 | IncreaseData,
14 | BanData,
15 | DecreaseData,
16 | NoticeGroupPoke,
17 | HandleMessage
18 | } from './interface'
19 |
20 | const database = { getDataBaseData, formSet }
21 | const { groups_config } = formSet
22 |
23 | // 身份验证事件
24 | const systemLoginSlider = () => process.stdin.once('data', input => bot.sliderLogin(input))
25 | // 监听群聊消息
26 | const messageGroup = async (data: OriginGroupData) => {
27 | const { group_id, sender, message_id, raw_message } = data
28 | const timestamp = +new Date()
29 | const row: PreGroupchatMessage = {
30 | userId: sender.user_id,
31 | msgId: message_id,
32 | timestamp,
33 | content: raw_message
34 | }
35 | // 写入数据
36 | addCacheMsgRows(group_id, row)
37 | const info: HandleMessage = {
38 | data,
39 | bot,
40 | segment,
41 | operations,
42 | database
43 | }
44 | handleMessage(info)
45 | }
46 | // 群成员增加时触发
47 | const noticeGroupIncrease = async (dataInit: IncreaseData) => {
48 | const { group_id, user_id } = dataInit
49 | const { setCard: { isAuto, content } } = await getDataBaseData(groups_config.name, groups_config.retrieveData)(group_id)
50 | if (!isAuto) return
51 | // 欢迎新成员
52 | bot.sendGroupMsg(dataInit.group_id, content)
53 | // 自动修改群昵称
54 | const card = await getChangeAllCard(bot, group_id, user_id)
55 | bot.setGroupCard(group_id, user_id, card)
56 | }
57 | // 群禁言事件
58 | const noticeGroupBan = async (data: BanData) => {
59 | const { group_id, operator_id, user_id, duration } = data
60 | const { data: o_data } = await bot.getGroupMemberInfo(group_id, operator_id)
61 | const operator_card = o_data.card ? o_data.card : o_data.nickname
62 | const ban = {
63 | // 全员禁言事件
64 | whole: async () => {
65 | if (duration === 0) return `<${operator_card}>关闭了全员禁言`
66 | return `<${operator_card}>开启了全员禁言`
67 | },
68 | // 管理员与成员之间发生的禁言事件
69 | single: async () => {
70 | // duration为0时代表此操作是解除禁言
71 | const { data: u_data } = await bot.getGroupMemberInfo(group_id, user_id)
72 | const user_card = u_data.card ? u_data.card : u_data.nickname
73 | if (duration === 0) return `${user_card},被<${operator_card}>解除了禁言`
74 | return `${user_card},被<${operator_card}>禁言${secToFormat(duration)}`
75 | }
76 | }
77 | const belong = user_id === 0 ? 'whole' : 'single'
78 | ban[belong]().then(message => bot.sendGroupMsg(group_id, message))
79 | }
80 | // 群成员减少事件
81 | const noticeGroupDecrease = (data: DecreaseData) => {
82 | const { group_id, operator_id, member } = data
83 | const f = async () => {
84 | const other_card = member.card ? member.card : member.nickname
85 | const other_id = member.user_id
86 | // 自己退群
87 | if (operator_id === other_id) return `<${other_card}>离开了群聊`
88 | // 被踢出群
89 | const gmlMap = await bot.gml.get(group_id)
90 | const operator_card = gmlMap.get(Number(operator_id)).card
91 | return `<${other_card}>被<${operator_card}>踢出了群聊`
92 | }
93 | f().then(message => bot.sendGroupMsg(group_id, message))
94 | }
95 | // 群戳一戳事件
96 | const noticeGroupPoke = async (data: NoticeGroupPoke) => {
97 | const { group_id, self_id, user_id, target_id, operator_id, action, suffix } = data
98 | // 如果戳一戳的目标人物是小秋,则不作任何处理
99 | if (self_id === user_id || self_id === target_id) return
100 | const { data: other } = await bot.getGroupMemberInfo(group_id, user_id)
101 | const { data: user } = await bot.getGroupMemberInfo(group_id, operator_id)
102 | const usernames = {
103 | nickname: user.nickname,
104 | card: user.card
105 | }
106 | const othernames = {
107 | nickname: other.nickname,
108 | card: other.card
109 | }
110 | const msg = [
111 | `${nicknameFormCard(usernames)}${action}${nicknameFormCard(othernames)}${suffix}`,
112 | `天啦噜 ${nicknameFormCard(usernames)}${action}${nicknameFormCard(othernames)}${suffix}`,
113 | `${nicknameFormCard(usernames)}${action}${nicknameFormCard(othernames)},但到底${action}什么呢?据小秋看来,应该是${nicknameFormCard(othernames)}${suffix}`
114 | ]
115 | bot.sendGroupMsg(group_id, returnOneOfContent(msg))
116 | }
117 | // 只触发被监听群聊的事件
118 | const processEvents = (name: GroupEvents, fn: Function) => {
119 | return async (data: OriginGroupData) => {
120 | const config = await getDataBaseData(groups_config.name, groups_config.retrieveData)(data.group_id)
121 | const { events } = config
122 | if (events[name]) fn(data)
123 | }
124 | }
125 | const registerEvents = (): void => {
126 | //监听并输入滑动验证码ticket
127 | bot.on('system.login.slider', systemLoginSlider)
128 | const events = [
129 | // 监听群聊消息
130 | ['message.group', messageGroup, 'message'],
131 | // 群成员增加时触发
132 | ['notice.group.increase', noticeGroupIncrease, 'increase'],
133 | // 群禁言事件
134 | ['notice.group.ban', noticeGroupBan, 'ban'],
135 | // 群踢人、退群事件
136 | ['notice.group.decrease', noticeGroupDecrease, 'decrease'],
137 | // 群戳一戳事件
138 | ['notice.group.poke', noticeGroupPoke, 'poke']
139 | ] as const
140 | events.forEach(ev => {
141 | const [name, fn, eventsAlias] = ev
142 | bot.on(name, processEvents(eventsAlias, fn))
143 | })
144 | }
145 |
146 | export { registerEvents }
147 |
--------------------------------------------------------------------------------
/eventsHandle/otherPlay/runtime/runtime.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * [js>]指令:
3 | * 用于在线运行JavaScript代码,由于不可防范因素众多,所以禁用了一些Node环境中的关键字
4 | */
5 | import fs from 'fs'
6 | import path from 'path'
7 | import { execFile } from 'child_process'
8 |
9 | import { self } from '../../../lib/user'
10 |
11 | import type { SendContent, CommandFn } from '../../../lib/interface'
12 |
13 | // 代码中不允许包含process等敏感词
14 | const sensitiveWord = [
15 | 'constructor',
16 | 'eval',
17 | 'process',
18 | 'global',
19 | 'fs',
20 | 'http',
21 | '__dirname',
22 | 'require',
23 | 'buffer',
24 | 'module',
25 | 'XMLHttpRequest',
26 | 'fetch',
27 | 'setTimeout',
28 | 'setInterval',
29 | 'exports',
30 | 'import',
31 | '全体成员',
32 | '@',
33 | 'url',
34 | 'until',
35 | 'assertion testing',
36 | 'asynchronous context tracking',
37 | 'async hooks',
38 | 'buffer',
39 | 'child processes',
40 | 'cluster',
41 | 'command-line options',
42 | 'corepack',
43 | 'crypto',
44 | 'debugger',
45 | 'diagnostics channel',
46 | 'dnc',
47 | 'domain',
48 | 'errors',
49 | 'events',
50 | 'http',
51 | 'http/2',
52 | 'https',
53 | 'inspector',
54 | 'internationalization',
55 | 'net',
56 | 'os',
57 | 'path',
58 | 'performance hooks',
59 | 'policies',
60 | 'punycode',
61 | 'query strings',
62 | 'readline',
63 | 'repl',
64 | 'report',
65 | 'stream',
66 | 'string decoder',
67 | 'test runner',
68 | 'timers',
69 | 'trace events',
70 | 'tty',
71 | 'udp/datagram',
72 | 'url',
73 | 'utilities',
74 | 'v8',
75 | 'vm',
76 | 'wasi',
77 | 'worker threads',
78 | 'zlib'
79 | ]
80 | const flagErrorReg = /系统消息ID123789456:检测本次代码运行是否发生错误。/gi
81 | const flagWasteReg = /系统消息ID123789456:计算代码所花费的时间。: (?