Skip to content

Commit

Permalink
feat(common): expose sub plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Mar 27, 2021
1 parent edbb368 commit 8f1ba35
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 300 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Context, Channel, Session, User, Argv } from 'koishi-core'
import { sleep, segment, template, makeArray, Time } from 'koishi-utils'
import { sleep, segment, template, makeArray, Time, simplify } from 'koishi-utils'

template.set('common', {
'expect-text': '请输入要发送的文本。',
Expand All @@ -19,97 +19,8 @@ interface RelayOptions {
lifespan?: number
}

export interface SenderConfig {
echo?: boolean
broadcast?: boolean
contextify?: boolean
operator?: string | string[]
relay?: RelayOptions | RelayOptions[]
}

export default function apply(ctx: Context, config: SenderConfig = {}) {
const dbctx = ctx.select('database')

config.echo !== false && ctx
.command('common/echo <message:text>', '向当前上下文发送消息', { authority: 2 })
.option('anonymous', '-a 匿名发送消息', { authority: 3 })
.option('forceAnonymous', '-A 匿名发送消息', { authority: 3 })
.option('escape', '-e 发送转义消息', { authority: 3 })
.action(async ({ options }, message) => {
if (!message) return template('common.expect-text')

if (options.escape) {
message = segment.unescape(message)
}

if (options.forceAnonymous) {
message = segment('anonymous') + message
} else if (options.anonymous) {
message = segment('anonymous', { ignore: true }) + message
}

return message
})

const operator = makeArray(config.operator)
if (operator.length) {
type FeedbackData = [sid: string, channelId: string]
const feedbacks: Record<number, FeedbackData> = {}

ctx.command('common/feedback <message:text>', '发送反馈信息给作者')
.userFields(['name', 'id'])
.action(async ({ session }, text) => {
if (!text) return template('common.expect-text')
const { username: name, userId } = session
const nickname = name === '' + userId ? userId : `${name} (${userId})`
const message = template('common.feedback-receive', nickname, text)
const delay = ctx.app.options.delay.broadcast
const data: FeedbackData = [session.sid, session.channelId]
for (let index = 0; index < operator.length; ++index) {
if (index && delay) await sleep(delay)
const [platform, userId] = Argv.parsePid(operator[index])
const id = await ctx.getBot(platform).sendPrivateMessage(userId, message)
feedbacks[id] = data
}
return template('common.feedback-success')
})

ctx.middleware((session, next) => {
const { quote, parsed } = session
if (!parsed.content || !quote) return next()
const data = feedbacks[quote.messageId]
if (!data) return next()
return ctx.bots[data[0]].sendMessage(data[1], parsed.content)
})
}

const relay = makeArray(config.relay)
const relayMap: Record<string, RelayOptions> = {}

async function sendRelay(session: Session, { destination, selfId, lifespan = Time.hour }: RelayOptions) {
const [platform, channelId] = Argv.parsePid(destination)
const bot = ctx.getBot(platform, selfId)
if (!session.parsed.content) return
const content = template('common.relay', session.username, session.parsed.content)
const id = await bot.sendMessage(channelId, content)
relayMap[id] = { source: destination, destination: session.cid, selfId: session.selfId, lifespan }
setTimeout(() => delete relayMap[id], lifespan)
}

ctx.middleware((session, next) => {
const { quote = {} } = session
const data = relayMap[quote.messageId]
if (data) return sendRelay(session, data)
const tasks: Promise<void>[] = []
for (const options of relay) {
if (session.cid !== options.source) continue
tasks.push(sendRelay(session, options).catch())
}
tasks.push(next())
return Promise.all(tasks)
})

config.broadcast !== false && dbctx
export function broadcast(ctx: Context) {
ctx.select('database')
.command('common/broadcast <message:text>', '全服广播', { authority: 4 })
.option('forced', '-f 无视 silent 标签进行广播')
.option('only', '-o 仅向当前 Bot 负责的群进行广播')
Expand All @@ -128,8 +39,10 @@ export default function apply(ctx: Context, config: SenderConfig = {}) {
}
await session.bot.broadcast(groups.map(g => g.id.slice(session.platform['length'] + 1)), message)
})
}

config.contextify !== false && dbctx
export function contextify(ctx: Context) {
ctx.select('database')
.command('common/contextify <message:text>', '在特定上下文中触发指令', { authority: 3 })
.alias('ctxf')
.userFields(['authority'])
Expand Down Expand Up @@ -188,3 +101,123 @@ export default function apply(ctx: Context, config: SenderConfig = {}) {
await sess.execute(message)
})
}

export function echo(ctx: Context) {
ctx.command('common/echo <message:text>', '向当前上下文发送消息', { authority: 2 })
.option('anonymous', '-a 匿名发送消息', { authority: 3 })
.option('forceAnonymous', '-A 匿名发送消息', { authority: 3 })
.option('escape', '-e 发送转义消息', { authority: 3 })
.action(async ({ options }, message) => {
if (!message) return template('common.expect-text')

if (options.escape) {
message = segment.unescape(message)
}

if (options.forceAnonymous) {
message = segment('anonymous') + message
} else if (options.anonymous) {
message = segment('anonymous', { ignore: true }) + message
}

return message
})
}

export function feedback(ctx: Context, operators: string[]) {
type FeedbackData = [sid: string, channelId: string]
const feedbacks: Record<number, FeedbackData> = {}

ctx.command('common/feedback <message:text>', '发送反馈信息给作者')
.userFields(['name', 'id'])
.action(async ({ session }, text) => {
if (!text) return template('common.expect-text')
const { username: name, userId } = session
const nickname = name === '' + userId ? userId : `${name} (${userId})`
const message = template('common.feedback-receive', nickname, text)
const delay = ctx.app.options.delay.broadcast
const data: FeedbackData = [session.sid, session.channelId]
for (let index = 0; index < operators.length; ++index) {
if (index && delay) await sleep(delay)
const [platform, userId] = Argv.parsePid(operators[index])
const id = await ctx.getBot(platform).sendPrivateMessage(userId, message)
feedbacks[id] = data
}
return template('common.feedback-success')
})

ctx.middleware((session, next) => {
const { quote, parsed } = session
if (!parsed.content || !quote) return next()
const data = feedbacks[quote.messageId]
if (!data) return next()
return ctx.bots[data[0]].sendMessage(data[1], parsed.content)
})
}

export function relay(ctx: Context, relays: RelayOptions[]) {
const relayMap: Record<string, RelayOptions> = {}

async function sendRelay(session: Session, { destination, selfId, lifespan = Time.hour }: RelayOptions) {
const [platform, channelId] = Argv.parsePid(destination)
const bot = ctx.getBot(platform, selfId)
if (!session.parsed.content) return
const content = template('common.relay', session.username, session.parsed.content)
const id = await bot.sendMessage(channelId, content)
relayMap[id] = { source: destination, destination: session.cid, selfId: session.selfId, lifespan }
setTimeout(() => delete relayMap[id], lifespan)
}

ctx.middleware((session, next) => {
const { quote = {} } = session
const data = relayMap[quote.messageId]
if (data) return sendRelay(session, data)
const tasks: Promise<void>[] = []
for (const options of relays) {
if (session.cid !== options.source) continue
tasks.push(sendRelay(session, options).catch())
}
tasks.push(next())
return Promise.all(tasks)
})
}

export interface Respondent {
match: string | RegExp
reply: string | ((...capture: string[]) => string)
}

export function respond(ctx: Context, respondents: Respondent[]) {
ctx.middleware((session, next) => {
const message = simplify(session.content)
for (const { match, reply } of respondents) {
const capture = typeof match === 'string' ? message === match && [message] : message.match(match)
if (capture) return session.send(typeof reply === 'string' ? reply : reply(...capture))
}
return next()
})
}

export interface BasicConfig {
echo?: boolean
broadcast?: boolean
contextify?: boolean
operator?: string | string[]
relay?: RelayOptions | RelayOptions[]
respondent?: Respondent | Respondent[]
}

export default function apply(ctx: Context, config: BasicConfig = {}) {
if (config.broadcast !== false) ctx.plugin(broadcast)
if (config.contextify !== false) ctx.plugin(contextify)
if (config.echo !== false) ctx.plugin(echo)

const operators = makeArray(config.operator)
if (operators.length) ctx.plugin(feedback, operators)

const relays = makeArray(config.relay)
if (relays.length) ctx.plugin(relay, relays)

const respondents = makeArray(config.respondent)
if (respondents.length) ctx.plugin(respond, respondents)
}

0 comments on commit 8f1ba35

Please sign in to comment.