Skip to content

Commit e7fe507

Browse files
committed
feat(core): 组件模块下支持 TelegramBotCommandsTelegramBotCommandTelegramBotCommandsUpdater 等与 bot commands 相关的API
1 parent c7790e1 commit e7fe507

File tree

7 files changed

+403
-4
lines changed

7 files changed

+403
-4
lines changed

simbot-component-telegram-api/supports.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,4 @@
4040
- [x] [SetWebhookApi](src/commonMain/kotlin/love/forte/simbot/telegram/api/update/SetWebhookApi.kt)
4141
- [user](src/commonMain/kotlin/love/forte/simbot/telegram/api/user)
4242
- [x] [GetUserProfilePhotosApi](src/commonMain/kotlin/love/forte/simbot/telegram/api/user/GetUserProfilePhotosApi.kt)
43-
- [ ] **Others aren’t listed**
43+
- [ ] **Others not listed**

simbot-component-telegram-core/src/commonMain/kotlin/love/forte/simbot/component/telegram/core/bot/TelegramBot.kt

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@ import love.forte.simbot.bot.GroupRelation
2323
import love.forte.simbot.bot.GuildRelation
2424
import love.forte.simbot.common.id.ID
2525
import love.forte.simbot.common.id.LongID.Companion.ID
26+
import love.forte.simbot.component.telegram.core.bot.command.TelegramBotCommands
27+
import love.forte.simbot.component.telegram.core.bot.command.TelegramBotCommandsUpdater
2628
import love.forte.simbot.component.telegram.core.component.TelegramComponent
2729
import love.forte.simbot.suspendrunner.ST
2830
import love.forte.simbot.telegram.api.update.Update
31+
import love.forte.simbot.telegram.type.BotCommandScope
2932
import love.forte.simbot.telegram.type.User
3033
import kotlin.coroutines.CoroutineContext
3134

@@ -98,14 +101,45 @@ public interface TelegramBot : Bot {
98101
source.pushUpdate(update, raw)
99102
}
100103

104+
/**
105+
* Fetches a [TelegramBotCommands] with [scope] and [languageCode].
106+
*
107+
* @param scope Scope of bot commands.
108+
* @param languageCode A two-letter ISO 639-1 language code of commands.
109+
*/
110+
@ST
111+
public suspend fun commands(scope: BotCommandScope?, languageCode: String?): TelegramBotCommands
112+
113+
/**
114+
* Fetches a [TelegramBotCommands].
115+
*/
116+
@ST
117+
public suspend fun commands(): TelegramBotCommands =
118+
commands(scope = null, languageCode = null)
119+
120+
/**
121+
* An updater for set commands.
122+
*
123+
* @see TelegramBotCommandsUpdater
124+
*/
125+
public val commandsUpdater: TelegramBotCommandsUpdater
101126

102127
override val groupRelation: GroupRelation?
103-
get() = null // TODO?
128+
get() = null
104129

105130
override val guildRelation: GuildRelation?
106-
get() = null // TODO?
131+
get() = null
107132

108133
override val contactRelation: ContactRelation?
109-
get() = null // TODO?
134+
get() = null
110135

111136
}
137+
138+
/**
139+
* Update bot commands by [TelegramBot.commandsUpdater]
140+
*
141+
* @see TelegramBot.commandsUpdater
142+
*/
143+
public suspend inline fun TelegramBot.updateCommands(
144+
block: TelegramBotCommandsUpdater.() -> Unit
145+
): TelegramBotCommands = commandsUpdater.also(block).update()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright (c) 2024. ForteScarlet.
3+
*
4+
* This file is part of simbot-component-telegram.
5+
*
6+
* simbot-component-telegram is free software: you can redistribute it and/or modify it under the terms
7+
* of the GNU Lesser General Public License as published by the Free Software Foundation,
8+
* either version 3 of the License, or (at your option) any later version.
9+
*
10+
* simbot-component-telegram is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11+
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License along with simbot-component-telegram.
15+
* If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
package love.forte.simbot.component.telegram.core.bot.command
19+
20+
import love.forte.simbot.ability.DeleteOption
21+
import love.forte.simbot.ability.DeleteSupport
22+
import love.forte.simbot.component.telegram.core.bot.TelegramBot
23+
import love.forte.simbot.telegram.type.BotCommand
24+
import love.forte.simbot.telegram.type.BotCommandScope
25+
import kotlin.jvm.JvmSynthetic
26+
27+
28+
/**
29+
* A set of Telegram's [BotCommand]s
30+
*
31+
* @author ForteScarlet
32+
*/
33+
public interface TelegramBotCommands : DeleteSupport, Iterable<TelegramBotCommand> {
34+
/**
35+
* The list of [TelegramBotCommand]
36+
*/
37+
public val values: List<TelegramBotCommand>
38+
39+
/**
40+
* Iterator of [values].
41+
*/
42+
override fun iterator(): Iterator<TelegramBotCommand> =
43+
values.iterator()
44+
45+
/**
46+
* A command scope used in the query.
47+
*/
48+
public val scope: BotCommandScope?
49+
50+
/**
51+
* A two-letter ISO 639-1 language code used in the query.
52+
*/
53+
public val languageCode: String?
54+
55+
/**
56+
* Delete this set of commands with [scope] and [languageCode].
57+
*/
58+
@JvmSynthetic
59+
override suspend fun delete(vararg options: DeleteOption)
60+
61+
/**
62+
* Get a [TelegramBotCommandsUpdater] with [scope] and [languageCode]
63+
* for update commands.
64+
*
65+
* @see TelegramBotCommandsUpdater
66+
*/
67+
public val updater: TelegramBotCommandsUpdater
68+
}
69+
70+
/**
71+
* Update bot commands by [TelegramBotCommands.updater]
72+
*
73+
* @see TelegramBotCommands.updater
74+
*/
75+
public suspend inline fun TelegramBotCommands.update(
76+
block: TelegramBotCommandsUpdater.() -> Unit
77+
): TelegramBotCommands = updater.also(block).update()
78+
79+
/**
80+
* A [BotCommand] of [TelegramBotCommands] from [TelegramBot.commands].
81+
*
82+
* @see TelegramBotCommands
83+
* @see BotCommand
84+
*/
85+
public interface TelegramBotCommand {
86+
/**
87+
* The source type [BotCommand]
88+
*/
89+
public val source: BotCommand
90+
91+
/**
92+
* Text of the command.
93+
*
94+
* @see BotCommand.command
95+
*/
96+
public val command: String
97+
get() = source.command
98+
99+
/**
100+
* Description of the command.
101+
*
102+
* @see BotCommand.description
103+
*/
104+
public val description: String
105+
get() = source.description
106+
}
107+
108+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (c) 2024. ForteScarlet.
3+
*
4+
* This file is part of simbot-component-telegram.
5+
*
6+
* simbot-component-telegram is free software: you can redistribute it and/or modify it under the terms
7+
* of the GNU Lesser General Public License as published by the Free Software Foundation,
8+
* either version 3 of the License, or (at your option) any later version.
9+
*
10+
* simbot-component-telegram is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11+
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License along with simbot-component-telegram.
15+
* If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
package love.forte.simbot.component.telegram.core.bot.command
19+
20+
import love.forte.simbot.suspendrunner.ST
21+
import love.forte.simbot.telegram.type.BotCommand
22+
import love.forte.simbot.telegram.type.BotCommandScope
23+
24+
25+
/**
26+
* An updater for set bot commands.
27+
*
28+
* @author ForteScarlet
29+
*/
30+
public interface TelegramBotCommandsUpdater {
31+
/**
32+
* the [BotCommandScope]
33+
*/
34+
public var scope: BotCommandScope?
35+
36+
/**
37+
* the [languageCode]
38+
*/
39+
public var languageCode: String?
40+
41+
/**
42+
* Commands for update.
43+
*/
44+
public var commands: MutableList<BotCommand>
45+
46+
/**
47+
* @see scope
48+
*/
49+
public fun scope(scope: BotCommandScope?): TelegramBotCommandsUpdater = apply {
50+
this.scope = scope
51+
}
52+
53+
/**
54+
* @see languageCode
55+
*/
56+
public fun languageCode(languageCode: String?): TelegramBotCommandsUpdater = apply {
57+
this.languageCode = languageCode
58+
}
59+
60+
/**
61+
* Add a command into [commands]
62+
* @see commands
63+
*/
64+
public fun addCommand(command: BotCommand): TelegramBotCommandsUpdater = apply {
65+
commands.add(command)
66+
}
67+
68+
/**
69+
* Add a command into [commands]
70+
* @see commands
71+
*/
72+
public fun addCommand(command: String, description: String): TelegramBotCommandsUpdater =
73+
addCommand(BotCommand(command, description))
74+
75+
/**
76+
* Execute setting and a new [TelegramBotCommands] that
77+
* values **directly** based on new commands is returned.
78+
*
79+
*/
80+
@ST
81+
public suspend fun update(): TelegramBotCommands
82+
}

simbot-component-telegram-core/src/commonMain/kotlin/love/forte/simbot/component/telegram/core/bot/internal/TelegramBotImpl.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ import love.forte.simbot.common.id.toLongOrNull
3131
import love.forte.simbot.component.telegram.core.bot.StdlibBot
3232
import love.forte.simbot.component.telegram.core.bot.TelegramBot
3333
import love.forte.simbot.component.telegram.core.bot.TelegramBotConfiguration
34+
import love.forte.simbot.component.telegram.core.bot.command.TelegramBotCommands
35+
import love.forte.simbot.component.telegram.core.bot.command.TelegramBotCommandsUpdater
36+
import love.forte.simbot.component.telegram.core.bot.internal.command.TelegramBotCommandsUpdaterImpl
37+
import love.forte.simbot.component.telegram.core.bot.internal.command.toTGCommands
3438
import love.forte.simbot.component.telegram.core.component.TelegramComponent
3539
import love.forte.simbot.component.telegram.core.event.TelegramUnsupportedEvent
3640
import love.forte.simbot.component.telegram.core.event.internal.TelegramChannelMessageEventImpl
@@ -42,6 +46,7 @@ import love.forte.simbot.event.EventDispatcher
4246
import love.forte.simbot.event.onEachError
4347
import love.forte.simbot.logger.LoggerFactory
4448
import love.forte.simbot.telegram.api.bot.GetMeApi
49+
import love.forte.simbot.telegram.api.bot.command.GetMyCommandsApi
4550
import love.forte.simbot.telegram.api.update.SuspendableUpdateDivider
4651
import love.forte.simbot.telegram.api.update.Update
4752
import love.forte.simbot.telegram.stdlib.bot.SubscribeSequence
@@ -111,9 +116,32 @@ internal class TelegramBotImpl(
111116
isStarted = true
112117
}
113118
}
119+
120+
override suspend fun commands(scope: BotCommandScope?, languageCode: String?): TelegramBotCommands {
121+
val commands = GetMyCommandsApi.create(
122+
scope = scope,
123+
languageCode = languageCode
124+
).requestDataBy(source)
125+
126+
return commands.toTGCommands(
127+
bot = this,
128+
scope = scope,
129+
languageCode = languageCode
130+
)
131+
}
132+
133+
override val commandsUpdater: TelegramBotCommandsUpdater
134+
get() = TelegramBotCommandsUpdaterImpl(
135+
bot = this,
136+
scope = null,
137+
languageCode = null
138+
)
114139
}
115140

116141

142+
143+
144+
117145
@OptIn(FragileSimbotAPI::class)
118146
internal fun subscribeInternalProcessor(
119147
bot: TelegramBotImpl,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright (c) 2024. ForteScarlet.
3+
*
4+
* This file is part of simbot-component-telegram.
5+
*
6+
* simbot-component-telegram is free software: you can redistribute it and/or modify it under the terms
7+
* of the GNU Lesser General Public License as published by the Free Software Foundation,
8+
* either version 3 of the License, or (at your option) any later version.
9+
*
10+
* simbot-component-telegram is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11+
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License along with simbot-component-telegram.
15+
* If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
package love.forte.simbot.component.telegram.core.bot.internal.command
19+
20+
import love.forte.simbot.ability.DeleteFailureException
21+
import love.forte.simbot.ability.DeleteOption
22+
import love.forte.simbot.ability.StandardDeleteOption
23+
import love.forte.simbot.component.telegram.core.bot.TelegramBot
24+
import love.forte.simbot.component.telegram.core.bot.command.TelegramBotCommand
25+
import love.forte.simbot.component.telegram.core.bot.command.TelegramBotCommands
26+
import love.forte.simbot.component.telegram.core.bot.command.TelegramBotCommandsUpdater
27+
import love.forte.simbot.telegram.api.bot.command.DeleteMyCommandsApi
28+
import love.forte.simbot.telegram.stdlib.bot.requestDataBy
29+
import love.forte.simbot.telegram.type.BotCommand
30+
import love.forte.simbot.telegram.type.BotCommandScope
31+
32+
internal class TelegramBotCommandsImpl(
33+
private val bot: TelegramBot,
34+
override val values: List<TelegramBotCommand>,
35+
override val scope: BotCommandScope?,
36+
override val languageCode: String?,
37+
) : TelegramBotCommands {
38+
override suspend fun delete(vararg options: DeleteOption) {
39+
runCatching {
40+
DeleteMyCommandsApi.create(
41+
scope = scope,
42+
languageCode = languageCode
43+
).requestDataBy(bot.source)
44+
}.onFailure { err ->
45+
if (StandardDeleteOption.IGNORE_ON_FAILURE !in options) {
46+
throw DeleteFailureException(err.message, err)
47+
}
48+
}
49+
}
50+
51+
override val updater: TelegramBotCommandsUpdater
52+
get() = TelegramBotCommandsUpdaterImpl(
53+
bot = bot,
54+
scope = scope,
55+
languageCode = languageCode
56+
)
57+
58+
override fun toString(): String =
59+
"TelegramBotCommands(scope=$scope, languageCode=$languageCode, values=$values)"
60+
}
61+
62+
internal fun Iterable<BotCommand>.toTGCommands(
63+
bot: TelegramBot,
64+
scope: BotCommandScope?,
65+
languageCode: String?,
66+
): TelegramBotCommandsImpl =
67+
TelegramBotCommandsImpl(
68+
bot,
69+
this.map { it.toTGCommand() },
70+
scope,
71+
languageCode
72+
)
73+
74+
/**
75+
*
76+
* @author ForteScarlet
77+
*/
78+
internal class TelegramBotCommandImpl(
79+
override val source: BotCommand
80+
) : TelegramBotCommand {
81+
override fun toString(): String {
82+
return "TelegramBotCommand(source=$source)"
83+
}
84+
}
85+
86+
internal fun BotCommand.toTGCommand(): TelegramBotCommandImpl =
87+
TelegramBotCommandImpl(this)

0 commit comments

Comments
 (0)