ScalaBot/scalabot-app/src/main/kotlin/ScalaBot.kt
LamGC ef37f3b2d7
fix(bot): 修复因机器人命令列表为空而导致命令列表自动更新报错的问题.
当机器人因扩展加载失败时, 将无法正常执行命令更新操作, 故添加空命令检查以避免该问题.
2022-05-17 19:56:42 +08:00

176 lines
6.0 KiB
Kotlin

package net.lamgc.scalabot
import io.prometheus.client.Counter
import io.prometheus.client.Gauge
import io.prometheus.client.Summary
import mu.KotlinLogging
import org.eclipse.aether.artifact.Artifact
import org.telegram.abilitybots.api.bot.AbilityBot
import org.telegram.abilitybots.api.db.DBContext
import org.telegram.abilitybots.api.objects.Ability
import org.telegram.abilitybots.api.toggle.BareboneToggle
import org.telegram.abilitybots.api.toggle.DefaultToggle
import org.telegram.telegrambots.bots.DefaultBotOptions
import org.telegram.telegrambots.meta.api.methods.commands.DeleteMyCommands
import org.telegram.telegrambots.meta.api.methods.commands.SetMyCommands
import org.telegram.telegrambots.meta.api.objects.Update
import org.telegram.telegrambots.meta.api.objects.commands.BotCommand
/**
* 可扩展 Bot.
* @property creatorId 机器人所有人的 Telegram 用户 Id. 可通过联系部分机器人来获取该信息.
* (e.g. [@userinfobot](http://t.me/userinfobot))
* @param db 机器人数据库对象. 用于状态机等用途.
* @param options AbilityBot 设置对象.
* @property extensions 扩展坐标集合.
*/
internal class ScalaBot(
db: DBContext,
options: DefaultBotOptions,
extensionFinders: Set<ExtensionPackageFinder>,
botConfig: BotConfig,
private val creatorId: Long = botConfig.account.creatorId,
val accountId: Long = botConfig.account.id,
val extensions: Set<Artifact> = botConfig.extensions
) :
AbilityBot(
botConfig.account.token,
botConfig.account.name,
db,
if (botConfig.disableBuiltInAbility)
BareboneToggle()
else
DefaultToggle(),
options
) {
private val extensionLoader = ExtensionLoader(
bot = this,
extensionFinders = extensionFinders
)
init {
log.info { "[Bot $botUsername] 正在加载扩展..." }
val extensionEntries = extensionLoader.getExtensions()
for (entry in extensionEntries) {
addExtension(entry.extension)
log.debug {
"[Bot $botUsername] 扩展包 `${entry.extensionArtifact}` 中的扩展 `${entry.extension::class.qualifiedName}` " +
"(由工厂类 `${entry.factoryClass.name}` 创建) 已注册."
}
}
log.info { "[Bot $botUsername] 扩展加载完成." }
}
override fun creatorId(): Long = creatorId
override fun onUpdateReceived(update: Update?) {
botUpdateCounter.labels(botUsername).inc()
botUpdateGauge.labels(botUsername).inc()
val timer = updateProcessTime.labels(botUsername).startTimer()
try {
super.onUpdateReceived(update)
} catch (e: Exception) {
exceptionHandlingCounter.labels(botUsername).inc()
throw e
} finally {
timer.observeDuration()
botUpdateGauge.labels(botUsername).dec()
}
}
/**
* 更新 Telegram Bot 的命令列表.
*
* 本方法将根据已注册的 [Ability] 更新 Telegram 中机器人的命令列表.
*
* 调用本方法前, 必须先调用一次 [registerAbilities], 否则无法获取 Ability 信息.
* @return 更新成功返回 `true`.
*/
fun updateCommandList(): Boolean {
if (abilities() == null) {
throw IllegalStateException("Abilities has not been initialized.")
}
val botCommands = abilities().values.map {
val abilityInfo = if (it.info() == null || it.info().trim().isEmpty()) {
log.warn { "[Bot $botUsername] Ability `${it.name()}` 没有说明信息." }
"(The command has no description)"
} else {
log.debug { "[Bot $botUsername] Ability `${it.name()}` info `${it.info()}`" }
it.info().trim()
}
BotCommand(it.name(), abilityInfo)
}
if (botCommands.isEmpty()) {
log.info { "Bot 没有任何命令, 命令列表更新已跳过." }
return true
}
val setMyCommands = SetMyCommands()
setMyCommands.commands = botCommands
return execute(DeleteMyCommands()) && execute(setMyCommands)
}
override fun onRegister() {
super.onRegister()
onlineBotGauge.inc()
}
override fun onClosing() {
onlineBotGauge.dec()
}
companion object {
@JvmStatic
private val log = KotlinLogging.logger { }
// ------------- Metrics -------------
@JvmStatic
private val botUpdateCounter = Counter.build()
.name("updates_total")
.help("Total number of updates received by all bots.")
.labelNames("bot_name")
.subsystem("telegrambots")
.register()
@JvmStatic
private val botUpdateGauge = Gauge.build()
.name("updates_in_progress")
.help("Number of updates in process by all bots.")
.labelNames("bot_name")
.subsystem("telegrambots")
.register()
@JvmStatic
private val onlineBotGauge = Gauge.build()
.name("bots_online")
.help("Number of bots Online.")
.subsystem("telegrambots")
.register()
@JvmStatic
private val updateProcessTime = Summary.build()
.name("update_process_duration_seconds")
.help(
"Time to process update. (This indicator includes the pre-processing of update by TelegramBots, " +
"so it may be different from the actual execution time of ability. " +
"It is not recommended to use it as the accurate execution time of ability)"
)
.labelNames("bot_name")
.subsystem("telegrambots")
.register()
@JvmStatic
private val exceptionHandlingCounter = Counter.build()
.name("updates_exception_handling")
.help("Number of exceptions during processing.")
.labelNames("bot_name")
.subsystem("telegrambots")
.register()
}
}