Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
Mirai
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Mirai
Commits
52dc100b
Commit
52dc100b
authored
Feb 06, 2020
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add docs
parent
e53dcfd2
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
134 additions
and
50 deletions
+134
-50
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
...moe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
+3
-3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
+50
-7
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
+20
-19
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LockFreeLinkedList.kt
...onMain/kotlin/net.mamoe.mirai/utils/LockFreeLinkedList.kt
+32
-1
mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/GentleImage.kt
...o-gentleman/src/main/kotlin/demo/gentleman/GentleImage.kt
+29
-20
No files found.
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
View file @
52dc100b
...
...
@@ -154,12 +154,12 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
val
groupInfo
=
mutableMapOf
<
Long
,
Int
>()
try
{
bot
.
logger
.
info
(
"开始加载群组列表与群成员列表"
)
val
troopData
=
FriendList
.
GetTroopListSimplify
(
val
troop
List
Data
=
FriendList
.
GetTroopListSimplify
(
bot
.
client
).
sendAndExpect
<
FriendList
.
GetTroopListSimplify
.
Response
>(
timeoutMillis
=
5000
)
// println("获取到群数量" + troopData.groups.size)
val
toGet
:
MutableMap
<
GroupImpl
,
ContactList
<
Member
>>
=
mutableMapOf
()
troopData
.
groups
.
forEach
{
troop
List
Data
.
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
(
"群组列表与群成员加载完成, 共 ${troop
List
Data.groups.size}个"
)
}
catch
(
e
:
Exception
)
{
bot
.
logger
.
error
(
"加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表"
)
println
(
e
.
message
)
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
View file @
52dc100b
...
...
@@ -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]
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
View file @
52dc100b
...
...
@@ -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
}
}
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LockFreeLinkedList.kt
View file @
52dc100b
...
...
@@ -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
...
...
mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/GentleImage.kt
View file @
52dc100b
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
(
1
0
*
1000
)
{
withTimeoutOrNull
(
2
0
*
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"
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment