diff --git a/.github/screenshot.png b/.github/screenshot.png new file mode 100644 index 0000000..ad046f8 Binary files /dev/null and b/.github/screenshot.png differ diff --git a/README.md b/README.md index 13b9e5d..afa0ee2 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,23 @@ [![maven-central](https://img.shields.io/maven-central/v/xyz.cssxsh.mirai/mirai-new-bing)](https://search.maven.org/artifact/xyz.cssxsh.mirai/mirai-new-bing) [![test](https://github.com/cssxsh/mirai-new-bing/actions/workflows/test.yml/badge.svg)](https://github.com/cssxsh/mirai-new-bing/actions/workflows/test.yml) +由于微软还未在中国大陆开放 `new bing` 的使用,以国内IP去访问 `bing` 会导致跳转 `404` 而无法使用 +故需要配置代理 `proxy` + +## 效果 + +![example](.github/screenshot.png) + +## 配置 + +`bing.yml` 基本配置 + +* `proxy` 代理, 协议支持 `socks` 和 `http`, 例如 `socks://127.0.0.1:7890` +* `timeout` API超时时间 +* `cookie` New Bing 网页 Cookie +* `chat_prefix` 触发前缀, 默认 `bing` +* `show_source_attributions` 输出来源信息, 默认 `true` + ## 安装 ### MCL 指令安装 diff --git a/src/main/kotlin/xyz/cssxsh/bing/NewBingChat.kt b/src/main/kotlin/xyz/cssxsh/bing/NewBingChat.kt index e787f1b..5611adb 100644 --- a/src/main/kotlin/xyz/cssxsh/bing/NewBingChat.kt +++ b/src/main/kotlin/xyz/cssxsh/bing/NewBingChat.kt @@ -10,6 +10,8 @@ public data class NewBingChat( val conversationId: String = "", @SerialName("conversationSignature") val conversationSignature: String = "", + @SerialName("uuid") + val uuid: String = "", @SerialName("index") var index: Int ) \ No newline at end of file diff --git a/src/main/kotlin/xyz/cssxsh/bing/NewBingClient.kt b/src/main/kotlin/xyz/cssxsh/bing/NewBingClient.kt index 95da481..2ccf20e 100644 --- a/src/main/kotlin/xyz/cssxsh/bing/NewBingClient.kt +++ b/src/main/kotlin/xyz/cssxsh/bing/NewBingClient.kt @@ -44,12 +44,14 @@ public open class NewBingClient(@PublishedApi internal val config: NewBingConfig } } } - protected open val format: Json = Json + protected open val format: Json = Json { + ignoreUnknownKeys = true + } protected val shared: MutableSharedFlow> = MutableSharedFlow() - @PublishedApi internal val uuid: UUID = UUID.randomUUID() @PublishedApi internal val logger: Logger = LoggerFactory.getLogger(this::class.java) public open suspend fun create(): NewBingChat { + val uuid: UUID = UUID.randomUUID() val response = http.get("https://www.bing.com/turing/conversation/create") { header("x-ms-client-request-id", uuid) header("x-ms-useragent", config.device) @@ -72,10 +74,13 @@ public open class NewBingClient(@PublishedApi internal val config: NewBingConfig conversation.result.message ?: conversation.result.value } + logger.debug(response.toString()) + return NewBingChat( clientId = conversation.clientId, conversationId = conversation.conversationId, conversationSignature = conversation.conversationSignature, + uuid = uuid.toString(), index = 0 ) } @@ -102,7 +107,7 @@ public open class NewBingClient(@PublishedApi internal val config: NewBingConfig 2 -> { if (logger.isDebugEnabled) logger.debug(item.toString()) launch { - shared.emit(chat.clientId to item) + shared.emit(chat.uuid to item) } } 3 -> { @@ -190,13 +195,11 @@ public open class NewBingClient(@PublishedApi internal val config: NewBingConfig }, block) } - public suspend fun send(chat: NewBingChat, text: String): String { + public suspend fun send(chat: NewBingChat, text: String) { websocket { bind(chat = chat) message(chat = chat, text = text) handle(chat = chat) } - - return chat.clientId } } \ No newline at end of file diff --git a/src/main/kotlin/xyz/cssxsh/bing/NewBingMessage.kt b/src/main/kotlin/xyz/cssxsh/bing/NewBingMessage.kt new file mode 100644 index 0000000..7b93730 --- /dev/null +++ b/src/main/kotlin/xyz/cssxsh/bing/NewBingMessage.kt @@ -0,0 +1,29 @@ +package xyz.cssxsh.bing + +import kotlinx.serialization.* + +@Serializable +public data class NewBingMessage( + @SerialName("author") + val author: String = "", + @SerialName("contentOrigin") + val contentOrigin: String = "", + @SerialName("createdAt") + val createdAt: String = "", + @SerialName("messageId") + val messageId: String = "", + @SerialName("offense") + val offense: String = "", + @SerialName("privacy") + val privacy: String? = null, + @SerialName("requestId") + val requestId: String = "", + @SerialName("sourceAttributions") + val sourceAttributions: List = emptyList(), + @SerialName("text") + val text: String = "", + @SerialName("timestamp") + val timestamp: String = "", + @SerialName("messageType") + val messageType: String = "", +) \ No newline at end of file diff --git a/src/main/kotlin/xyz/cssxsh/bing/NewBingSourceAttribution.kt b/src/main/kotlin/xyz/cssxsh/bing/NewBingSourceAttribution.kt new file mode 100644 index 0000000..cbb0e13 --- /dev/null +++ b/src/main/kotlin/xyz/cssxsh/bing/NewBingSourceAttribution.kt @@ -0,0 +1,13 @@ +package xyz.cssxsh.bing + +import kotlinx.serialization.* + +@Serializable +public data class NewBingSourceAttribution( + @SerialName("providerDisplayName") + val providerDisplayName: String = "", + @SerialName("searchQuery") + val searchQuery: String = "", + @SerialName("seeMoreUrl") + val seeMoreUrl: String = "" +) \ No newline at end of file diff --git a/src/main/kotlin/xyz/cssxsh/mirai/bing/MiraiNewBingConfig.kt b/src/main/kotlin/xyz/cssxsh/mirai/bing/MiraiNewBingConfig.kt index f8a38ec..adbf183 100644 --- a/src/main/kotlin/xyz/cssxsh/mirai/bing/MiraiNewBingConfig.kt +++ b/src/main/kotlin/xyz/cssxsh/mirai/bing/MiraiNewBingConfig.kt @@ -3,7 +3,7 @@ package xyz.cssxsh.mirai.bing import net.mamoe.mirai.console.data.* import xyz.cssxsh.bing.* -public object MiraiNewBingConfig : ReadOnlyPluginConfig("openai"), NewBingConfig { +public object MiraiNewBingConfig : ReadOnlyPluginConfig("bing"), NewBingConfig { @ValueName("proxy") override val proxy: String by value("socks://127.0.0.1:7890") @@ -43,4 +43,7 @@ public object MiraiNewBingConfig : ReadOnlyPluginConfig("openai"), NewBingConfig @ValueName("chat_prefix") public val prefix: String by value("bing") + + @ValueName("show_source_attributions") + public val source: Boolean by value(true) } \ No newline at end of file diff --git a/src/main/kotlin/xyz/cssxsh/mirai/bing/MiraiNewBingListener.kt b/src/main/kotlin/xyz/cssxsh/mirai/bing/MiraiNewBingListener.kt index 9bd3828..7fcbc08 100644 --- a/src/main/kotlin/xyz/cssxsh/mirai/bing/MiraiNewBingListener.kt +++ b/src/main/kotlin/xyz/cssxsh/mirai/bing/MiraiNewBingListener.kt @@ -4,9 +4,11 @@ import kotlinx.coroutines.* import kotlinx.serialization.json.* import net.mamoe.mirai.console.command.CommandSender.Companion.toCommandSender import net.mamoe.mirai.console.permission.* +import net.mamoe.mirai.console.permission.PermissionService.Companion.hasPermission import net.mamoe.mirai.contact.* import net.mamoe.mirai.event.* import net.mamoe.mirai.event.events.* +import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.* import xyz.cssxsh.bing.* import kotlin.coroutines.* @@ -16,19 +18,27 @@ internal object MiraiNewBingListener : SimpleListenerHost() { private val client = object : NewBingClient(config = MiraiNewBingConfig) { init { launch { - shared.collect { (clientId, data) -> + shared.collect { (uuid, data) -> val item = data["item"] as? JsonObject ?: return@collect val messages = item["messages"] as? JsonArray ?: return@collect - for (message in messages) { - message as? JsonObject ?: continue - val author = message["author"]?.jsonPrimitive?.content ?: continue - if ("bot" != author) continue - val (id, _) = chats.entries.find { it.value.clientId == clientId } ?: continue + for (element in messages) { + val message = format.decodeFromJsonElement(NewBingMessage.serializer(), element) + if ("bot" != message.author) continue + if ("InternalSearchQuery" == message.messageType) continue + val (id, _) = chats.entries.find { it.value.uuid == uuid } ?: continue val subject = contacts[id] ?: continue - val text = message["text"]?.jsonPrimitive?.content ?: continue launch { - subject.sendMessage(text) + subject.sendMessage(buildMessageChain { + appendLine(message.text) + if (MiraiNewBingConfig.source && message.sourceAttributions.isEmpty().not()) { + appendLine() + var index = 1 + for (attribution in message.sourceAttributions) { + appendLine("[^${index++}^] ${attribution.providerDisplayName} ${attribution.seeMoreUrl}") + } + } + }) } } } @@ -56,13 +66,15 @@ internal object MiraiNewBingListener : SimpleListenerHost() { suspend fun MessageEvent.handle() { val content = message.contentToString() if (content.startsWith(MiraiNewBingConfig.prefix).not()) return + val commander = toCommandSender() + if (commander.hasPermission(chat).not()) return - val id = toCommandSender().permitteeId.asString() + val id = commander.permitteeId.asString() val cache = chats[id] ?: client.create() chats[id] = cache contacts[id] = subject launch { - client.send(cache, content.removePrefix(MiraiNewBingConfig.prefix)) + client.send(chat = cache, text = content.removePrefix(MiraiNewBingConfig.prefix)) } } } \ No newline at end of file