mirror of
https://github.com/mamoe/mirai.git
synced 2025-04-25 04:50:26 +08:00
Add docs
This commit is contained in:
parent
e53dcfd26b
commit
52dc100b19
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network
mirai-core/src/commonMain/kotlin/net.mamoe.mirai
mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman
@ -154,12 +154,12 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
val groupInfo = mutableMapOf<Long, Int>()
|
||||
try {
|
||||
bot.logger.info("开始加载群组列表与群成员列表")
|
||||
val troopData = FriendList.GetTroopListSimplify(
|
||||
val troopListData = FriendList.GetTroopListSimplify(
|
||||
bot.client
|
||||
).sendAndExpect<FriendList.GetTroopListSimplify.Response>(timeoutMillis = 5000)
|
||||
// println("获取到群数量" + troopData.groups.size)
|
||||
val toGet: MutableMap<GroupImpl, ContactList<Member>> = mutableMapOf()
|
||||
troopData.groups.forEach {
|
||||
troopListData.groups.forEach {
|
||||
val contactList = ContactList(LockFreeLinkedList<Member>())
|
||||
val groupInfoResponse = try {
|
||||
TroopManagement.GetGroupOperationInfo(
|
||||
@ -204,7 +204,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
bot.logger.info("群组列表与群成员加载完成, 共 ${troopData.groups.size}个")
|
||||
bot.logger.info("群组列表与群成员加载完成, 共 ${troopListData.groups.size}个")
|
||||
} catch (e: Exception) {
|
||||
bot.logger.error("加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表")
|
||||
println(e.message)
|
||||
|
@ -6,6 +6,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.io.OutputStream
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.IoBuffer
|
||||
import kotlinx.io.core.use
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.ContactList
|
||||
@ -14,23 +15,33 @@ import net.mamoe.mirai.contact.QQ
|
||||
import net.mamoe.mirai.data.AddFriendResult
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.network.BotNetworkHandler
|
||||
import net.mamoe.mirai.utils.LoginFailedException
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.WeakRef
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.io.transferTo
|
||||
|
||||
/**
|
||||
* 机器人对象. 一个机器人实例登录一个 QQ 账号.
|
||||
* Mirai 为多账号设计, 可同时维护多个机器人.
|
||||
*
|
||||
* 注: Bot 为全协程实现, 没有其他任务时若不使用 [awaitDisconnection], 主线程将会退出.
|
||||
*
|
||||
* @see Contact
|
||||
*/
|
||||
abstract class Bot : CoroutineScope {
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
companion object {
|
||||
/**
|
||||
* 复制一份此时的 [Bot] 实例列表.
|
||||
*/
|
||||
val instances: List<WeakRef<Bot>> get() = BotImpl.instances.toList()
|
||||
|
||||
/**
|
||||
* 遍历每一个 [Bot] 实例
|
||||
*/
|
||||
inline fun forEachInstance(block: (Bot) -> Unit) = BotImpl.forEachInstance(block)
|
||||
|
||||
/**
|
||||
* 获取一个 [Bot] 实例, 找不到则 [NoSuchElementException]
|
||||
*/
|
||||
fun instanceWhose(qq: Long): Bot = BotImpl.instanceWhose(qq = qq)
|
||||
}
|
||||
|
||||
@ -53,7 +64,7 @@ abstract class Bot : CoroutineScope {
|
||||
// region contacts
|
||||
|
||||
/**
|
||||
* 机器人的好友列表.
|
||||
* 机器人的好友列表. 它将与服务器同步更新
|
||||
*/
|
||||
abstract val qqs: ContactList<QQ>
|
||||
|
||||
@ -63,6 +74,22 @@ abstract class Bot : CoroutineScope {
|
||||
@Deprecated(message = "这个函数有歧义. 它获取的是好友, 却名为 getQQ", replaceWith = ReplaceWith("getFriend(id)"))
|
||||
fun getQQ(id: Long): QQ = getFriend(id)
|
||||
|
||||
/**
|
||||
* 获取一个好友或一个群.
|
||||
* 在一些情况下这可能会造成歧义. 请考虑后使用.
|
||||
*/
|
||||
operator fun get(id: Long): Contact {
|
||||
return this.qqs.getOrNull(id) ?: this.groups.getOrNull(id) ?: throw NoSuchElementException("contact id $id")
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否有这个 id 的好友或群.
|
||||
* 在一些情况下这可能会造成歧义. 请考虑后使用.
|
||||
*/
|
||||
operator fun contains(id: Long): Boolean {
|
||||
return this.qqs.contains(id) || this.groups.contains(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个好友对象. 若没有这个好友, 则会抛出异常 [NoSuchElementException]
|
||||
*/
|
||||
@ -86,7 +113,7 @@ abstract class Bot : CoroutineScope {
|
||||
*/
|
||||
abstract fun getGroup(id: Long): Group
|
||||
|
||||
// 目前还不能构造群对象. 这将在以后支持
|
||||
// TODO 目前还不能构造群对象. 这将在以后支持
|
||||
|
||||
// endregion
|
||||
|
||||
@ -97,6 +124,11 @@ abstract class Bot : CoroutineScope {
|
||||
*/
|
||||
abstract val network: BotNetworkHandler
|
||||
|
||||
/**
|
||||
* 挂起直到 [Bot] 下线.
|
||||
*/
|
||||
suspend inline fun awaitDisconnection() = network.awaitDisconnection()
|
||||
|
||||
/**
|
||||
* 登录, 或重新登录.
|
||||
* 不建议调用这个函数.
|
||||
@ -111,8 +143,12 @@ abstract class Bot : CoroutineScope {
|
||||
|
||||
// region actions
|
||||
|
||||
@Deprecated("内存使用效率十分低下", ReplaceWith("this.download()"), DeprecationLevel.WARNING)
|
||||
abstract suspend fun Image.downloadAsByteArray(): ByteArray
|
||||
|
||||
/**
|
||||
* 将图片下载到内存中 (使用 [IoBuffer.Pool])
|
||||
*/
|
||||
abstract suspend fun Image.download(): ByteReadPacket
|
||||
|
||||
/**
|
||||
@ -131,7 +167,9 @@ abstract class Bot : CoroutineScope {
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* 关闭这个 [Bot], 停止一切相关活动. 不可重新登录.
|
||||
* 关闭这个 [Bot], 停止一切相关活动. 所有引用都会被释放.
|
||||
*
|
||||
* 注: 不可重新登录. 必须重新实例化一个 [Bot].
|
||||
*
|
||||
* @param cause 原因. 为 null 时视为正常关闭, 非 null 时视为异常关闭
|
||||
*/
|
||||
@ -139,9 +177,14 @@ abstract class Bot : CoroutineScope {
|
||||
|
||||
// region extensions
|
||||
|
||||
@Deprecated(message = "这个函数有歧义, 将在不久后删除", replaceWith = ReplaceWith("getFriend(this.toLong())"))
|
||||
fun Int.qq(): QQ = getFriend(this.toLong())
|
||||
@Deprecated(message = "这个函数有歧义, 将在不久后删除", replaceWith = ReplaceWith("getFriend(this)"))
|
||||
fun Long.qq(): QQ = getFriend(this)
|
||||
|
||||
final override fun toString(): String {
|
||||
return "Bot(${uin})"
|
||||
}
|
||||
|
||||
/**
|
||||
* 需要调用者自行 close [output]
|
||||
|
@ -33,29 +33,29 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
|
||||
final override val logger: MiraiLogger by lazy { configuration.logger ?: DefaultLogger("Bot($uin)").also { configuration.logger = it } }
|
||||
|
||||
init {
|
||||
@Suppress("LeakingThis")
|
||||
instances.addLast(this)
|
||||
instances.addLast(this.weakRef())
|
||||
}
|
||||
|
||||
companion object {
|
||||
@PublishedApi
|
||||
internal val instances: LockFreeLinkedList<Bot> = LockFreeLinkedList()
|
||||
internal val instances: LockFreeLinkedList<WeakRef<Bot>> = LockFreeLinkedList()
|
||||
|
||||
inline fun forEachInstance(block: (Bot) -> Unit) = instances.forEach(block)
|
||||
inline fun forEachInstance(block: (Bot) -> Unit) = instances.forEach {
|
||||
it.get()?.let(block)
|
||||
}
|
||||
|
||||
fun instanceWhose(qq: Long): Bot {
|
||||
instances.forEach {
|
||||
@Suppress("PropertyName")
|
||||
if (it.uin == qq) {
|
||||
return it
|
||||
it.get()?.let { bot ->
|
||||
if (bot.uin == qq) {
|
||||
return bot
|
||||
}
|
||||
}
|
||||
}
|
||||
throw NoSuchElementException()
|
||||
}
|
||||
}
|
||||
|
||||
final override fun toString(): String = "Bot(${uin})"
|
||||
|
||||
// region network
|
||||
|
||||
final override val network: N get() = _network
|
||||
@ -134,16 +134,17 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
override fun close(cause: Throwable?) {
|
||||
if (cause == null) {
|
||||
network.close()
|
||||
this.botJob.complete()
|
||||
groups.delegate.clear()
|
||||
qqs.delegate.clear()
|
||||
} else {
|
||||
network.close(cause)
|
||||
this.botJob.completeExceptionally(cause)
|
||||
groups.delegate.clear()
|
||||
qqs.delegate.clear()
|
||||
kotlin.runCatching {
|
||||
if (cause == null) {
|
||||
network.close()
|
||||
this.botJob.complete()
|
||||
} else {
|
||||
network.close(cause)
|
||||
this.botJob.completeExceptionally(cause)
|
||||
}
|
||||
}
|
||||
groups.delegate.clear()
|
||||
qqs.delegate.clear()
|
||||
instances.removeIf { it.get()?.uin == this.uin }
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ open class LockFreeLinkedList<E> {
|
||||
/**
|
||||
* 过滤并获取, 获取不到则添加一个元素.
|
||||
*/
|
||||
inline fun filteringGetOrAdd(filter: (E) -> Boolean, noinline supplier: () -> E): E {
|
||||
fun filteringGetOrAdd(filter: (E) -> Boolean, supplier: () -> E): E {
|
||||
val node = LazyNode(tail, supplier)
|
||||
|
||||
while (true) {
|
||||
@ -152,6 +152,36 @@ open class LockFreeLinkedList<E> {
|
||||
}, { it !is Tail })
|
||||
}.dropLast(4)
|
||||
|
||||
@Suppress("DuplicatedCode")
|
||||
fun removeIf(filter: (E) -> Boolean) {
|
||||
while (true) {
|
||||
val before = head.iterateBeforeFirst { it.isValidElementNode() && filter(it.nodeValue) }
|
||||
val toRemove = before.nextNode
|
||||
if (toRemove === tail) {
|
||||
return
|
||||
}
|
||||
if (toRemove.isRemoved()) {
|
||||
continue
|
||||
}
|
||||
@Suppress("BooleanLiteralArgument") // false positive
|
||||
if (!toRemove.removed.compareAndSet(false, true)) {
|
||||
// logically remove: all the operations will recognize this node invalid
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
// physically remove: try to fix the link
|
||||
var next: Node<E> = toRemove.nextNode
|
||||
while (next !== tail && next.isRemoved()) {
|
||||
next = next.nextNode
|
||||
}
|
||||
if (before.nextNodeRef.compareAndSet(toRemove, next)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DuplicatedCode")
|
||||
open fun remove(element: E): Boolean {
|
||||
while (true) {
|
||||
val before = head.iterateBeforeNodeValue(element)
|
||||
@ -162,6 +192,7 @@ open class LockFreeLinkedList<E> {
|
||||
if (toRemove.isRemoved()) {
|
||||
continue
|
||||
}
|
||||
@Suppress("BooleanLiteralArgument") // false positive
|
||||
if (!toRemove.removed.compareAndSet(false, true)) {
|
||||
// logically remove: all the operations will recognize this node invalid
|
||||
continue
|
||||
|
@ -1,13 +1,13 @@
|
||||
package demo.gentleman
|
||||
|
||||
import com.alibaba.fastjson.JSON
|
||||
import com.alibaba.fastjson.JSONObject
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.UnstableDefault
|
||||
import kotlinx.serialization.json.Json
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.message.uploadAsImage
|
||||
import org.jsoup.Jsoup
|
||||
import kotlin.random.Random
|
||||
|
||||
class GentleImage(val contact: Contact, val keyword: String) {
|
||||
|
||||
@ -15,34 +15,43 @@ class GentleImage(val contact: Contact, val keyword: String) {
|
||||
|
||||
val seImage: Deferred<Image> by lazy { getImage(1) }
|
||||
|
||||
@UseExperimental(UnstableDefault::class)
|
||||
fun getImage(r18: Int): Deferred<Image> {
|
||||
return GlobalScope.async {
|
||||
withTimeoutOrNull(10 * 1000) {
|
||||
withTimeoutOrNull(20 * 1000) {
|
||||
withContext(Dispatchers.IO) {
|
||||
|
||||
@Serializable
|
||||
data class Setu(
|
||||
val url: String,
|
||||
val pid: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Result(
|
||||
val data: List<Setu>
|
||||
)
|
||||
|
||||
val result =
|
||||
JSON.parseObject(
|
||||
Jsoup.connect("https://api.lolicon.app/setu/?r18=$r18" + if (keyword.isNotBlank()) "&keyword=$keyword&num=100" else "").ignoreContentType(
|
||||
true
|
||||
).timeout(
|
||||
10_0000
|
||||
).get().body().text()
|
||||
Json.plain.parse(
|
||||
Result.serializer(),
|
||||
Jsoup.connect("https://api.lolicon.app/setu/?r18=$r18" + if (keyword.isNotBlank()) "&keyword=$keyword&num=10" else "")
|
||||
.ignoreContentType(true)
|
||||
.userAgent(UserAgent.randomUserAgent)
|
||||
.proxy("127.0.0.1", 1088)
|
||||
.timeout(10_0000)
|
||||
.get().body().text()
|
||||
)
|
||||
|
||||
val url: String
|
||||
val pid: String
|
||||
val data = result.getJSONArray("data")
|
||||
with(JSONObject(data.getJSONObject(Random.nextInt(0, data.size)))) {
|
||||
url = this.getString("url")
|
||||
pid = this.getString("pid")
|
||||
}
|
||||
|
||||
val setu = result.data.random()
|
||||
Jsoup
|
||||
.connect(url)
|
||||
.connect(setu.url)
|
||||
.followRedirects(true)
|
||||
.timeout(180_000)
|
||||
.ignoreContentType(true)
|
||||
.userAgent("Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; ja-jp) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27")
|
||||
.referrer("https://www.pixiv.net/member_illust.php?mode=medium&illust_id=$pid")
|
||||
.referrer("https://www.pixiv.net/member_illust.php?mode=medium&illust_id=${setu.pid}")
|
||||
.proxy("127.0.0.1", 1088)
|
||||
.ignoreHttpErrors(true)
|
||||
.maxBodySize(10000000)
|
||||
.execute().also { check(it.statusCode() == 200) { "Failed to download image" } }
|
||||
|
Loading…
Reference in New Issue
Block a user