mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 10:30:13 +08:00
commit
126a4914eb
@ -1,17 +1,17 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
id 'org.jetbrains.kotlin.multiplatform'
|
id 'org.jetbrains.kotlin.multiplatform'
|
||||||
|
id 'kotlin-android-extensions'
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 29
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "net.mamoe.mirai.demo"
|
applicationId "net.mamoe.mirai.demo"
|
||||||
minSdkVersion 23
|
minSdkVersion 21
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
@ -53,10 +53,11 @@ dependencies {
|
|||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
|
|
||||||
androidTestImplementation 'junit:junit:4.12'
|
androidTestImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
|
||||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
|
||||||
|
|
||||||
|
|
||||||
def anko_version = "0.10.8"
|
def anko_version = "0.10.8"
|
||||||
implementation "org.jetbrains.anko:anko-commons:$anko_version"
|
implementation "org.jetbrains.anko:anko-commons:$anko_version"
|
||||||
|
|
||||||
|
implementation group: 'io.ktor', name: 'ktor-client-android', version: '1.2.5'
|
||||||
|
implementation("io.ktor:ktor-client-android:1.2.5")
|
||||||
}
|
}
|
@ -2,9 +2,11 @@
|
|||||||
package="net.mamoe.mirai.demo">
|
package="net.mamoe.mirai.demo">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="net.mamoe.mirai.demo.MyApplication"
|
android:label="@string/app_name"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
@ -18,5 +20,6 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<service android:name=".MiraiService"/>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package net.mamoe.mirai.demo
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
|
||||||
|
interface LoginCallback {
|
||||||
|
|
||||||
|
suspend fun onCaptcha(bitmap: Bitmap)
|
||||||
|
suspend fun onSuccess()
|
||||||
|
suspend fun onFailed()
|
||||||
|
suspend fun onMessage(message:String)
|
||||||
|
}
|
@ -0,0 +1,121 @@
|
|||||||
|
package net.mamoe.mirai.demo
|
||||||
|
|
||||||
|
import android.app.ProgressDialog
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.ServiceConnection
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import io.ktor.util.cio.writeChannel
|
||||||
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.event.subscribeFriendMessages
|
||||||
|
import net.mamoe.mirai.login
|
||||||
|
import net.mamoe.mirai.network.protocol.tim.packet.login.requireSuccess
|
||||||
|
import net.mamoe.mirai.utils.DefaultLogger
|
||||||
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
|
import net.mamoe.mirai.utils.PlatformLogger
|
||||||
|
import net.mamoe.mirai.utils.SimpleLogger
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class MainActivity : AppCompatActivity(),LoginCallback {
|
||||||
|
|
||||||
|
|
||||||
|
private lateinit var progressDialog : ProgressDialog
|
||||||
|
|
||||||
|
override suspend fun onCaptcha(bitmap: Bitmap) {
|
||||||
|
withContext(Dispatchers.Main){
|
||||||
|
ll_captcha.visibility = View.VISIBLE
|
||||||
|
iv_captcha.setImageBitmap(bitmap)
|
||||||
|
needCaptcha = true
|
||||||
|
if (progressDialog.isShowing){
|
||||||
|
progressDialog.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun onMessage(message:String) {
|
||||||
|
withContext(Dispatchers.Main){
|
||||||
|
msg.text = "${msg.text}\n$message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun onSuccess() {
|
||||||
|
withContext(Dispatchers.Main){
|
||||||
|
Toast.makeText(this@MainActivity,"登录成功",Toast.LENGTH_SHORT).show()
|
||||||
|
if (progressDialog.isShowing){
|
||||||
|
progressDialog.dismiss()
|
||||||
|
}
|
||||||
|
ll_captcha.visibility = View.GONE
|
||||||
|
et_pwd.visibility = View.GONE
|
||||||
|
et_qq.visibility = View.GONE
|
||||||
|
bt_login.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun onFailed() {
|
||||||
|
withContext(Dispatchers.Main){
|
||||||
|
Toast.makeText(this@MainActivity,"登录失败",Toast.LENGTH_SHORT).show()
|
||||||
|
if (progressDialog.isShowing){
|
||||||
|
progressDialog.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var binder: MiraiService.MiraiBinder? = null
|
||||||
|
|
||||||
|
var needCaptcha = false
|
||||||
|
|
||||||
|
|
||||||
|
private val conn = object : ServiceConnection {
|
||||||
|
|
||||||
|
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||||
|
binder = service as MiraiService.MiraiBinder?
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServiceDisconnected(name: ComponentName?) {
|
||||||
|
binder = null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_main)
|
||||||
|
val intent = Intent(this, MiraiService::class.java)
|
||||||
|
startService(intent)
|
||||||
|
bindService(intent, conn, Service.BIND_AUTO_CREATE)
|
||||||
|
progressDialog = ProgressDialog(this)
|
||||||
|
bt_login.setOnClickListener {
|
||||||
|
if (!progressDialog.isShowing){
|
||||||
|
progressDialog.show()
|
||||||
|
}
|
||||||
|
binder?.setCallback(this)
|
||||||
|
if (!needCaptcha){
|
||||||
|
val qq = et_qq.text.toString().toUInt()
|
||||||
|
val pwd = et_pwd.text.toString()
|
||||||
|
binder?.startLogin(qq, pwd)
|
||||||
|
}else{
|
||||||
|
val captcha = et_captcha.text.toString()
|
||||||
|
binder?.setCaptcha(captcha)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
unbindService(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,159 @@
|
|||||||
|
package net.mamoe.mirai.demo
|
||||||
|
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.os.Binder
|
||||||
|
import android.os.IBinder
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.contact.QQ
|
||||||
|
import net.mamoe.mirai.event.subscribeMessages
|
||||||
|
import net.mamoe.mirai.login
|
||||||
|
import net.mamoe.mirai.message.Image
|
||||||
|
import net.mamoe.mirai.network.protocol.tim.packet.event.GroupMessage
|
||||||
|
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
|
class MiraiService : Service() {
|
||||||
|
|
||||||
|
private var mCaptchaDeferred: CompletableDeferred<String>? = null
|
||||||
|
|
||||||
|
private var mBot: Bot? = null
|
||||||
|
|
||||||
|
private var mCaptcha = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
mCaptchaDeferred?.complete(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var mBinder: MiraiBinder? = null
|
||||||
|
|
||||||
|
private var mCallback: WeakReference<LoginCallback>? = null
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
mBinder = MiraiBinder()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun login(qq: UInt, pwd: String) {
|
||||||
|
GlobalScope.launch {
|
||||||
|
mBot = Bot(qq, pwd).apply {
|
||||||
|
val loginResult = login {
|
||||||
|
captchaSolver = {
|
||||||
|
val byteArray = byteArrayOf()
|
||||||
|
it.readFully(byteArray, 0, it.writeRemaining)
|
||||||
|
val bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
|
||||||
|
mCallback?.get()?.onCaptcha(bitmap)
|
||||||
|
mCaptchaDeferred?.await()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (loginResult == LoginResult.SUCCESS) {
|
||||||
|
mCallback?.get()?.onSuccess()
|
||||||
|
} else {
|
||||||
|
mCallback?.get()?.onFailed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mBot!!.subscribeMessages {
|
||||||
|
content({ true }) {
|
||||||
|
mCallback?.get()?.onMessage("收到来自${sender.id}的消息")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当接收到消息 == "你好" 时就回复 "你好!"
|
||||||
|
"你好" reply "你好!"
|
||||||
|
|
||||||
|
// 当消息 == "查看 subject" 时, 执行 lambda
|
||||||
|
case("查看 subject") {
|
||||||
|
if (subject is QQ) {
|
||||||
|
reply("消息主体为 QQ, 你在跟发私聊消息")
|
||||||
|
} else {
|
||||||
|
reply("消息主体为 Group, 你在群里发消息")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在回复的时候, 一般使用 subject 来作为回复对象.
|
||||||
|
// 因为当群消息时, subject 为这个群.
|
||||||
|
// 当好友消息时, subject 为这个好友.
|
||||||
|
// 所有在 MessagePacket(也就是此时的 this 指代的对象) 中实现的扩展方法, 如刚刚的 "reply", 都是以 subject 作为目标
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 当消息里面包含这个类型的消息时
|
||||||
|
has<Image> {
|
||||||
|
// this: MessagePacket
|
||||||
|
// message: MessageChain
|
||||||
|
// sender: QQ
|
||||||
|
// it: String (MessageChain.toString)
|
||||||
|
|
||||||
|
if (this is GroupMessage) {
|
||||||
|
//如果是群消息
|
||||||
|
// group: Group
|
||||||
|
this.group.sendMessage("你在一个群里")
|
||||||
|
// 等同于 reply("你在一个群里")
|
||||||
|
}
|
||||||
|
|
||||||
|
reply("图片, ID= ${message[Image].id}")//获取第一个 Image 类型的消息
|
||||||
|
reply(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
"123" containsReply "你的消息里面包含 123"
|
||||||
|
|
||||||
|
|
||||||
|
// 当收到 "我的qq" 就执行 lambda 并回复 lambda 的返回值 String
|
||||||
|
"我的qq" reply { sender.id.toString() }
|
||||||
|
|
||||||
|
|
||||||
|
// 当消息前缀为 "我是" 时
|
||||||
|
startsWith("我是", removePrefix = true) {
|
||||||
|
// it: 删除了消息前缀 "我是" 后的消息
|
||||||
|
// 如一条消息为 "我是张三", 则此时的 it 为 "张三".
|
||||||
|
|
||||||
|
reply("你是$it")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 当消息中包含 "复读" 时
|
||||||
|
contains("复读") {
|
||||||
|
reply(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 自定义的 filter, filter 中 it 为转为 String 的消息.
|
||||||
|
// 也可以用任何能在处理时使用的变量, 如 subject, sender, message
|
||||||
|
content({ it.length == 3 }) {
|
||||||
|
reply("你发送了长度为 3 的消息")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent?): IBinder? {
|
||||||
|
return mBinder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inner class MiraiBinder : Binder() {
|
||||||
|
|
||||||
|
fun startLogin(qq: UInt, pwd: String) {
|
||||||
|
login(qq, pwd)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCaptcha(captcha: String) {
|
||||||
|
mCaptcha = captcha
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCallback(callback: LoginCallback) {
|
||||||
|
mCallback = WeakReference(callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,57 +0,0 @@
|
|||||||
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE")
|
|
||||||
|
|
||||||
package net.mamoe.mirai.demo
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.widget.LinearLayout
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import net.mamoe.mirai.Bot
|
|
||||||
import net.mamoe.mirai.event.subscribeFriendMessages
|
|
||||||
import net.mamoe.mirai.login
|
|
||||||
import net.mamoe.mirai.network.protocol.tim.packet.login.requireSuccess
|
|
||||||
import net.mamoe.mirai.utils.DefaultLogger
|
|
||||||
import net.mamoe.mirai.utils.PlatformLogger
|
|
||||||
import net.mamoe.mirai.utils.SimpleLogger
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.PrintStream
|
|
||||||
import kotlin.properties.Delegates
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
private val Throwable.stacktraceString: String
|
|
||||||
get() = ByteArrayOutputStream().also { printStackTrace(PrintStream(it)) }.toString()
|
|
||||||
|
|
||||||
class MyApplication : Application()
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
|
||||||
private var rootLayout: LinearLayout by Delegates.notNull()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setContentView(R.layout.activity_main)
|
|
||||||
rootLayout = findViewById(R.id.main_view)
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun initializeBot(qq: UInt, password: String) {
|
|
||||||
DefaultLogger = {
|
|
||||||
PlatformLogger(it) + SimpleLogger { message, e ->
|
|
||||||
// TODO: 2019/11/6
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val bot = Bot(qq, password).apply {
|
|
||||||
login {
|
|
||||||
captchaSolver = {
|
|
||||||
|
|
||||||
"ABCD"
|
|
||||||
}
|
|
||||||
}.requireSuccess()
|
|
||||||
}
|
|
||||||
|
|
||||||
bot.subscribeFriendMessages {
|
|
||||||
"Hello" reply "Hello Mirai!"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,62 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/main_view"
|
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"/>
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="46dp"
|
||||||
|
android:inputType="number"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:ems="15"
|
||||||
|
android:hint="请输入QQ号"
|
||||||
|
android:id="@+id/et_qq" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="46dp"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:hint="请输入密码"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:id="@+id/et_pwd" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_captcha"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/et_captcha"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="46dp"
|
||||||
|
android:hint="输入验证码"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_captcha"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginLeft="5dp"
|
||||||
|
android:scaleType="fitXY"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="登录"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:id="@+id/bt_login"
|
||||||
|
android:background="#3F51B5"
|
||||||
|
android:textColor="#FFFFFF"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/msg"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Mirai</string>
|
||||||
|
</resources>
|
Loading…
Reference in New Issue
Block a user