diff --git a/src/main/kotlin/OracleNetworkExtension.kt b/src/main/kotlin/OracleNetworkExtension.kt new file mode 100644 index 0000000..399a04c --- /dev/null +++ b/src/main/kotlin/OracleNetworkExtension.kt @@ -0,0 +1,410 @@ +package net.lamgc.scext.oraclemanager + +import com.oracle.bmc.core.VirtualNetworkClient +import com.oracle.bmc.core.model.CreatePublicIpDetails +import com.oracle.bmc.core.model.CreateVcnDetails +import com.oracle.bmc.core.model.Vcn +import com.oracle.bmc.core.requests.* +import com.oracle.bmc.model.BmcException +import mu.KotlinLogging +import net.lamgc.scalabot.extension.BotExtensionFactory +import org.telegram.abilitybots.api.bot.BaseAbilityBot +import org.telegram.abilitybots.api.objects.Reply +import org.telegram.abilitybots.api.util.AbilityExtension +import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageReplyMarkup +import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageText +import java.io.File + +class OracleNetworkExtensionFactory : BotExtensionFactory { + override fun createExtensionInstance(bot: BaseAbilityBot, shareDataFolder: File): AbilityExtension { + return OracleNetworkExtension(bot) + } +} + + +@Suppress("unused") +class OracleNetworkExtension(private val bot: BaseAbilityBot) : AbilityExtension { + + fun networkMenu(): Reply = callbackQueryOf("oc_network_menu") { bot, upd -> + val keyboardBuilder = InlineKeyboardGroupBuilder() + .rowButton { + text("VCN 列表") + callbackData(upd.callbackQuery.callbackData.next("oc_network_vcn_list")) + } + .newRow() + .addButton { + text("IPv4 管理") + callbackData(upd.callbackQuery.callbackData.next("oc_network_ipv4pub_manage")) + } + .addButton { + text("IPv6 管理") + callbackData(upd.callbackQuery.callbackData.next("oc_network_ipv4pub_manage")) + } + .then() + .addBackButton(upd.callbackQuery.callbackData.next("oc_account_manager")) + EditMessageReplyMarkup.builder() + .chatId(upd.callbackQuery.message.chatId.toString()) + .messageId(upd.callbackQuery.message.messageId) + .replyMarkup(keyboardBuilder.build()) + .build() + .execute(bot.silent()) + } + + + fun listVCNs(): Reply = callbackQueryOf("oc_network_vcn_list") { bot, upd -> + val profile = getProfileByCallback(upd.callbackQuery.callbackData) + val client = VirtualNetworkClient(profile.getAuthenticationDetailsProvider()) + + val vcns = client.listVcns( + ListVcnsRequest.builder() + .compartmentId(profile.tenantId) + .build() + ).items + vcns.removeIf { it.lifecycleState == Vcn.LifecycleState.Terminated } + val keyboardBuilder = InlineKeyboardGroupBuilder() + for (vcn in vcns) { + logger.debug { "VCN: $vcn" } + keyboardBuilder.rowButton { + text(vcn.displayName) + callbackData(upd.callbackQuery.callbackData.next( + newAction = "oc_network_vcn_manage", + newExtraData = jsonObjectOf { + JsonFields.VcnId += vcn.id + JsonFields.VcnDisplayName += vcn.displayName + } + )) + } + } + + keyboardBuilder.rowButton { + text("*** 新建 VCN ***") + callbackData( + upd.callbackQuery.callbackData.next( + newAction = "oc_network_vcn_create" + ) + ) + } + + keyboardBuilder.addBackButton(upd.callbackQuery.callbackData.next("oc_account_manager")) + EditMessageText.builder() + .chatId(upd.callbackQuery.message.chatId.toString()) + .messageId(upd.callbackQuery.message.messageId) + .replyMarkup(keyboardBuilder.build()) + .text( + """ + Oracle 账号 ${profile.name} 有以下 VCN: + """.trimIndent() + ) + .build() + .execute(bot.sender()) + } + + fun createVcn(): Reply = callbackQueryHandleOf("oc_network_vcn_create") { + val profile = getProfileByCallback(callbackData) + val createVcnRequest = CreateVcnRequest.builder() + .createVcnDetails( + CreateVcnDetails.builder() + .compartmentId(profile.tenantId) + .build() + ) + .build() + + val client = VirtualNetworkClient(profile.getAuthenticationDetailsProvider()) + // 使用 try 代码块包括 client 请求 + try { + val vcn = client.createVcn(createVcnRequest).vcn + EditMessageText.builder() + .chatId(chatId) + .messageId(upd.callbackQuery.message.messageId) + .text("创建 VCN ${vcn.displayName} 成功!") + .replyMarkup( + InlineKeyboardGroupBuilder().addBackButton(callbackData.next("oc_network_vcn_list")).build() + ) + .build() + .execute(bot.sender()) + } catch (e: BmcException) { + EditMessageText.builder() + .chatId(chatId) + .messageId(upd.callbackQuery.message.messageId) + .text("创建 VCN 失败!\n错误信息:${e.message}") + .replyMarkup( + InlineKeyboardGroupBuilder().addBackButton(callbackData.next("oc_network_vcn_list")).build() + ) + .build() + .execute(bot.sender()) + } + + } + + fun manageVcn(): Reply = callbackQueryOf("oc_network_vcn_manage") { bot, upd -> + val profile = getProfileByCallback(upd.callbackQuery.callbackData) + val client = VirtualNetworkClient(profile.getAuthenticationDetailsProvider()) + val vcnId = upd.callbackQuery.callbackData.extraData[JsonFields.VcnId].asString + val vcn = try { + client.getVcn( + GetVcnRequest.builder() + .vcnId(vcnId) + .build() + ).vcn + } catch (e: Exception) { + logger.error(e) { "获取 VCN 信息时发生错误." } + bot.silent().send("获取 VCN 信息时发生错误,请重试一次。", upd.callbackQuery.message.chatId) + return@callbackQueryOf + } + + val keyboardGroup = InlineKeyboardGroupBuilder() + .rowButton { + text("子网列表") + callbackData(upd.callbackQuery.callbackData.next("oc_network_vcn_subnet_list")) + } + .newRow() + .addButton { + text("网络安全组") + callbackData(upd.callbackQuery.callbackData.next("oc_network_vcn_nsg_manage")) + } + .addButton { + text("安全列表") + callbackData(upd.callbackQuery.callbackData.next("oc_network_vcn_sl_manage")) + } + .then() + .rowButton { + text("设置") + callbackData(upd.callbackQuery.callbackData.next("oc_network_vcn_edit")) + } + + EditMessageText.builder() + .chatId(upd.callbackQuery.message.chatId.toString()) + .messageId(upd.callbackQuery.message.messageId) + .replyMarkup( + keyboardGroup.addBackButton( + callback = upd.callbackQuery.callbackData.next("oc_network_vcn_list") + ).build() + ) + .text( + """ + --------- [ VCN ] --------- + ${vcn.displayName} + 状态:${vcn.lifecycleState} + 私有 IPv4 段:${vcn.cidrBlock}(共 ${vcn.cidrBlocks.size} 段) + IPv6 段:${vcn.ipv6CidrBlocks.firstOrNull()}(共 ${vcn.ipv6CidrBlocks.size} 段) + """.trimIndent() + ) + .build().execute(bot) + } + + fun listSubnet(): Reply = callbackQueryOf("oc_network_vcn_subnet_list") { bot, upd -> + val profile = getProfileByCallback(upd.callbackQuery.callbackData) + val client = VirtualNetworkClient(profile.getAuthenticationDetailsProvider()) + val vcnId = upd.callbackQuery.callbackData.extraData[JsonFields.VcnId].asString + val vcnDisplayName = upd.callbackQuery.callbackData.extraData[JsonFields.VcnDisplayName].asString + val subnets = client.listSubnets( + ListSubnetsRequest.builder() + .compartmentId(profile.tenantId) + .vcnId(vcnId) + .build() + ).items + + val keyboardBuilder = InlineKeyboardGroupBuilder() + + for (subnet in subnets) { + keyboardBuilder.rowButton { + text("${subnet.displayName}【${subnet.cidrBlock}】") + callbackData(upd.callbackQuery.callbackData.next("oc_network_vcn_subnet_edit", + newExtraData = jsonObjectOf { + JsonFields.SubnetId += subnet.id + } + )) + } + } + + EditMessageText.builder() + .chatId(upd.callbackQuery.message.chatId.toString()) + .messageId(upd.callbackQuery.message.messageId) + .replyMarkup( + keyboardBuilder.addBackButton( + callback = upd.callbackQuery.callbackData.next("oc_network_vcn_manage") + ).build() + ) + .text("VCN $vcnDisplayName 有以下子网(Subnet):") + .build().execute(bot) + } + + fun editSubnet(): Reply = callbackQueryOf("oc_network_vcn_subnet_edit") { bot, upd -> + val profile = getProfileByCallback(upd.callbackQuery.callbackData) + val client = VirtualNetworkClient(profile.getAuthenticationDetailsProvider()) + val subnetId = upd.callbackQuery.callbackData.extraData[JsonFields.SubnetId].asString + val subnet = client.getSubnet(GetSubnetRequest.builder().subnetId(subnetId).build()).subnet + + val keyboardGroup = InlineKeyboardGroupBuilder() + .newRow() + .addButton { + text("私网 IPv4") + callbackData(upd.callbackQuery.callbackData.next("oc_network_subnet_ipv4pri_manage")) + } + .then() + .rowButton { + text("IPv6") + callbackData(upd.callbackQuery.callbackData.next("oc_network_subnet_ipv6_manage")) + } + .rowButton { + text("绑定网络安全组") + callbackData(upd.callbackQuery.callbackData.next("oc_network_subnet_bind_nsg")) + } + .rowButton { + text("设置") + callbackData(upd.callbackQuery.callbackData.next("oc_network_subnet_edit")) + } + + EditMessageText.builder() + .chatId(upd.callbackQuery.message.chatId.toString()) + .messageId(upd.callbackQuery.message.messageId) + .replyMarkup( + keyboardGroup.addBackButton( + callback = upd.callbackQuery.callbackData.next("oc_network_vcn_subnet_list") + ).build() + ) + .text( + """ + ${subnet.displayName} + 私有 IPv4 段:${subnet.cidrBlock} + IPv6 段:${subnet.ipv6CidrBlock} + 已激活安全列表数:${subnet.securityListIds.size} + 所属 VCN 名称:${upd.callbackQuery.callbackData.extraData[JsonFields.VcnDisplayName].asString} + """.trimIndent() + ) + .build().execute(bot) + + } + + fun listSubnetPublicIpv4(): Reply = callbackQueryOf("oc_network_ipv4pub_manage") { bot, upd -> + val profile = getProfileByCallback(upd.callbackQuery.callbackData) + val client = VirtualNetworkClient(profile.getAuthenticationDetailsProvider()) + val publicIps = client.listPublicIps( + ListPublicIpsRequest.builder() + .compartmentId(profile.tenantId) + .scope(ListPublicIpsRequest.Scope.Region) + .build() + ).items + + val keyboardBuilder = InlineKeyboardGroupBuilder() + for (publicIp in publicIps) { + keyboardBuilder.rowButton { + text("${publicIp.displayName}【${publicIp.ipAddress}】") + callbackData( + upd.callbackQuery.callbackData.next("oc_network_vcn_ipv4pub_edit", + newExtraData = jsonObjectOf { + JsonFields.PublicIpId += publicIp.id + }) + ) + } + } + + keyboardBuilder.rowButton { + text("*** 新建公网 IP ***") + callbackData(upd.callbackQuery.callbackData.next("oc_network_vcn_ipv4pub_add")) + }.addBackButton( + callback = upd.callbackQuery.callbackData.next("oc_network_vcn_subnet_edit") + ) + + EditMessageText.builder() + .replyTo(upd.callbackQuery) + .text("Oracle账号 ${profile.name} 当前区域有以下预留的公共 IP:") + .replyMarkup(keyboardBuilder.build()) + .build().execute(bot) + } + + fun createNewIpv4Public(): Reply = callbackQueryOf("oc_network_vcn_ipv4pub_add") { bot, upd -> + val profile = getProfileByCallback(upd.callbackQuery.callbackData) + val client = VirtualNetworkClient(profile.getAuthenticationDetailsProvider()) + + val newPublicIp = try { + client.createPublicIp( + CreatePublicIpRequest.builder() + .createPublicIpDetails( + CreatePublicIpDetails.builder() + .compartmentId(profile.tenantId) + .lifetime(CreatePublicIpDetails.Lifetime.Reserved) + .build() + ) + .build() + ).publicIp + } catch (e: BmcException) { + logger.error(e) { "请求创建 PublicIp 时发生错误." } + bot.silent().send("创建 Ip 时发生错误。\n${e.message}", upd.callbackQuery.message.chatId) + return@callbackQueryOf + } + + EditMessageText.builder() + .replyTo(upd.callbackQuery) + .text("已创建新的保留公共 IP:${newPublicIp.ipAddress}") + .replyMarkup( + InlineKeyboardGroupBuilder() + .addBackButton(upd.callbackQuery.callbackData.next("oc_network_ipv4pub_manage")) + .build() + ) + .build().execute(bot) + } + + fun editIpv4Public(): Reply = callbackQueryOf("oc_network_vcn_ipv4pub_edit") { bot, upd -> + val profile = getProfileByCallback(upd.callbackQuery.callbackData) + val client = VirtualNetworkClient(profile.getAuthenticationDetailsProvider()) + val publicIp = client.getPublicIp( + GetPublicIpRequest.builder() + .publicIpId(upd.callbackQuery.callbackData.extraData[JsonFields.PublicIpId].asString) + .build() + ).publicIp + + val keyboardBuilder = InlineKeyboardGroupBuilder() + .rowButton { + text("更改显示名称") + callbackData("EMPTY") + } + .rowButton { + text("删除 IP 地址") + callbackData("EMPTY") + } + .addBackButton(upd.callbackQuery.callbackData.next("oc_network_ipv4pub_manage")) + EditMessageText.builder() + .replyTo(upd.callbackQuery) + .replyMarkup(keyboardBuilder.build()) + .text( + """ + ---------- [ 公共 IP ] ---------- + ${publicIp.displayName} + IP 地址:${publicIp.ipAddress} + 当前状态:${publicIp.lifecycleState} + 可用范围:${publicIp.scope} + 可用域:${publicIp.availabilityDomain} + """.trimIndent() + ) + .build().execute(bot) + } + + fun editVcn(): Reply = callbackQueryOf("oc_network_vcn_edit") { bot, upd -> + val keyboardBuilder = InlineKeyboardGroupBuilder() + .rowButton { + text("更改名称") + callbackData(upd.callbackQuery.callbackData.next("oc_network_vcn_change_name")) + } + .rowButton { + text("删除 VCN") + callbackData(upd.callbackQuery.callbackData.next("oc_network_vcn_delete")) + } + + EditMessageReplyMarkup.builder() + .chatId(upd.callbackQuery.message.chatId.toString()) + .messageId(upd.callbackQuery.message.messageId) + .replyMarkup( + keyboardBuilder.addBackButton( + callback = upd.callbackQuery.callbackData.next("oc_network_vcn_manage") + ).build() + ) + .build().execute(bot) + } + + + companion object { + private val logger = KotlinLogging.logger { } + } +} +