mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-09 18:00:33 +08:00
commit
126a4914eb
@ -1,17 +1,17 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.multiplatform'
|
||||
id 'kotlin-android-extensions'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
defaultConfig {
|
||||
applicationId "net.mamoe.mirai.demo"
|
||||
minSdkVersion 23
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
@ -53,10 +53,11 @@ dependencies {
|
||||
testImplementation '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"
|
||||
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">
|
||||
|
||||
<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
|
||||
android:name="net.mamoe.mirai.demo.MyApplication"
|
||||
android:label="@string/app_name"
|
||||
android:allowBackup="true"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
@ -18,5 +20,6 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service android:name=".MiraiService"/>
|
||||
</application>
|
||||
</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"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/main_view"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:scrollbars="vertical"
|
||||
android:layout_width="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