diff --git a/.gitignore b/.gitignore index c9c3308..498f526 100644 --- a/.gitignore +++ b/.gitignore @@ -164,3 +164,5 @@ target/ token.txt Main2.java +Main3.java +aliyun/* diff --git a/README.md b/README.md index 28a7970..d2bd3b9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # java-wechaty ![Java CI with Maven](https://github.com/wechaty/java-wechaty/workflows/Java%20CI%20with%20Maven/badge.svg) -[![Java Version](https://img.shields.io/maven-central/v/io.github.wechaty/wechaty?label=Java)](https://mvnrepository.com/artifact/io.github.wechaty/wechaty) +[![Java Version](https://img.shields.io/maven-central/v/io.github.wechaty/wechaty?label=Maven)](https://mvnrepository.com/artifact/io.github.wechaty/wechaty) -![Java Wechaty](https://wechaty.github.io/java-wechaty/images/java-wechaty.png) +![Java Wechaty](docs/images/java-wechaty.png) [![Java Wechaty Getting Started](https://img.shields.io/badge/Java%20Wechaty-Getting%20Started-orange)](https://github.com/wechaty/java-wechaty-getting-started) [![Wechaty in Kotlin](https://img.shields.io/badge/Wechaty-Kotlin-orange)](https://github.com/wechaty/java-wechaty) @@ -15,12 +15,6 @@ Wechaty is a RPA SDK for Wechat **Individual** Account that can help you create a chatbot in 6 lines of Java. -## WORK IN PROGRESS - -Work in progress... - -Please come back after 4 weeks... - ## Voice of the Developers > "Wechaty is a great solution, I believe there would be much more users recognize it." [link](https://github.com/Wechaty/wechaty/pull/310#issuecomment-285574472) @@ -44,7 +38,7 @@ See more at [Wiki:Voice Of Developer](https://github.com/Wechaty/wechaty/wiki/Vo Wechaty is used in many ChatBot projects by thousands of developers. If you want to talk with other developers, just scan the following QR Code in WeChat with secret code _java wechaty_, join our **Wechaty Java Developers' Home**. -![Wechaty Java Developers' Home](https://wechaty.github.io/wechaty/images/bot-qr-code.png) +![Wechaty Friday.BOT QR Code](https://wechaty.js.org/img/friday-qrcode.svg) Scan now, because other Wechaty Java developers want to talk with you too! (secret code: _java wechaty_) @@ -234,7 +228,7 @@ If you are interested in the translation and want to look at how it works, it wi - [ ] Unit Tests - [ ] Documentation 1. [ ] Class WechatyPuppetHostie - - TS SLOC(909): + - TS SLOC(909): - [x] Code - [ ] Unit Tests - [ ] Documentation @@ -278,6 +272,20 @@ mvn install wechaty ### master +### v0.4 (Jun 19, 2020) + +Java(Kotlin) Wechaty **BETA** Released! + +Read more from our Multi-language Wechaty Beta Release event from our blog: + +- [Multi Language Wechaty Beta Release Announcement!](https://wechaty.js.org/2020/06/19/multi-language-wechaty-beta-release/) + +### v0.1.4 (June 13 2020) +1. use `PuppetManager` to manage multi puppet implementations. +2. add mock puppet. +3. remove puppet implementations from wechaty pom. Which implementation to use depends on which implementation jar in your pom. +4. fix some bugs. + ### v0.1.3 (June 6 2020) 1. support plugins! @@ -315,6 +323,9 @@ We decided to use Kotlin to develop the Java Wechaty! - [Java Wechaty](https://github.com/wechaty/java-wechaty) - Java WeChaty Conversational AI Chatbot SDK for Wechat Individual Accounts (Java) - [Scala Wechaty](https://github.com/wechaty/scala-wechaty) - Scala WeChaty Conversational AI Chatbot SDK for WechatyIndividual Accounts (Scala) +## Stargazers over time + +[![Stargazers over time](https://starchart.cc/wechaty/java-wechaty.svg)](https://starchart.cc/wechaty/java-wechaty) ## Badge @@ -324,11 +335,23 @@ We decided to use Kotlin to develop the Java Wechaty! [![Wechaty in Kotlin](https://img.shields.io/badge/Wechaty-Kotlin-orange)](https://github.com/wechaty/java-wechaty) ``` +## Contributors + +[![contributors](https://sourcerer.io/fame/huan/wechaty/java-wechaty/images/0)](https://sourcerer.io/fame/huan/wechaty/java-wechaty/links/0) +[![contributors](https://sourcerer.io/fame/huan/wechaty/java-wechaty/images/1)](https://sourcerer.io/fame/huan/wechaty/java-wechaty/links/1) +[![contributors](https://sourcerer.io/fame/huan/wechaty/java-wechaty/images/2)](https://sourcerer.io/fame/huan/wechaty/java-wechaty/links/2) +[![contributors](https://sourcerer.io/fame/huan/wechaty/java-wechaty/images/3)](https://sourcerer.io/fame/huan/wechaty/java-wechaty/links/3) +[![contributors](https://sourcerer.io/fame/huan/wechaty/java-wechaty/images/4)](https://sourcerer.io/fame/huan/wechaty/java-wechaty/links/4) +[![contributors](https://sourcerer.io/fame/huan/wechaty/java-wechaty/images/5)](https://sourcerer.io/fame/huan/wechaty/java-wechaty/links/5) +[![contributors](https://sourcerer.io/fame/huan/wechaty/java-wechaty/images/6)](https://sourcerer.io/fame/huan/wechaty/java-wechaty/links/6) +[![contributors](https://sourcerer.io/fame/huan/wechaty/java-wechaty/images/7)](https://sourcerer.io/fame/huan/wechaty/java-wechaty/links/7) + ## Committers - [@redmaple1](https://github.com/redmaple1) Xiaoya Ren +- [@huan](https://github.com/huan) - Huan LI (李卓桓) -## Author +## Creator - [@diaozxin007](https://github.com/diaozxin007) diaozxin@gmail.com - Website: [犀利豆的博客](https://xilidou.com/) diff --git a/VERSION b/VERSION index 17e51c3..1d0ba9e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.1 +0.4.0 diff --git a/examples/pom.xml b/examples/pom.xml index d708add..f550c80 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -1,14 +1,20 @@ + io.github.wechaty wechaty-parent - 1.0.0-SNAPSHOT + 0.1.5-SNAPSHOT + 4.0.0 wechaty-example + + wechaty/example + jar + 1.0.0-SNAPSHOT 1.3.72 @@ -17,6 +23,7 @@ UTF-8 true 0.10.1 + 0.1.5-SNAPSHOT @@ -24,7 +31,7 @@ io.github.wechaty wechaty - 0.1.3-SNAPSHOT + ${wechaty.version} org.apache.logging.log4j @@ -35,7 +42,7 @@ org.apache.logging.log4j log4j-core - 2.13.1 + 2.13.2 org.apache.logging.log4j @@ -60,6 +67,30 @@ java-wechaty-plugin-contrib 1.0.0-SNAPSHOT + + org.apache.commons + commons-lang3 + + + + com.aliyun + aliyun-java-sdk-core + 4.5.2 + + + + + com.aliyun + aliyun-java-sdk-imagerecog + 1.0.7 + + + + + com.aliyun.oss + aliyun-sdk-oss + 3.4.2 + diff --git a/examples/src/main/java/io/github/wechaty/example/Main.java b/examples/src/main/java/io/github/wechaty/example/Main.java index 41975a8..4a0f34e 100644 --- a/examples/src/main/java/io/github/wechaty/example/Main.java +++ b/examples/src/main/java/io/github/wechaty/example/Main.java @@ -1,12 +1,7 @@ package io.github.wechaty.example; -import io.github.wechaty.LoginListener; -import io.github.wechaty.MessageListener; import io.github.wechaty.Wechaty; -import io.github.wechaty.filebox.FileBox; -import io.github.wechaty.io.github.wechaty.schemas.EventEnum; -import io.github.wechaty.user.Contact; import io.github.wechaty.user.Room; import io.github.wechaty.utils.QrcodeUtils; import okhttp3.OkHttpClient; diff --git a/pom.xml b/pom.xml index 60779cd..05f8401 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.github.wechaty wechaty-parent pom - 0.1.3-SNAPSHOT + 0.1.5-SNAPSHOT kotlin-wechaty @@ -31,6 +31,7 @@ wechaty-puppet wechaty-puppet-hostie wechaty-puppet-mock + @@ -76,6 +77,24 @@ slf4j-api 1.7.30 + + org.apache.logging.log4j + log4j-core + 2.13.2 + test + + + org.apache.logging.log4j + log4j-slf4j-impl + 2.13.1 + test + + + com.lmax + disruptor + 3.4.2 + test + org.apache.commons @@ -150,6 +169,11 @@ javafaker 1.0.2 + + org.reflections + reflections + 0.9.10 + @@ -157,25 +181,44 @@ - org.apache.maven.plugins - maven-gpg-plugin - 1.6 + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + - sign-artifacts - verify + generate-sources - sign + add-source - - --pinentry-mode - loopback - + + src/main/kotlin + + + + + + + + + + + + + + + + + + + + + diff --git a/wechaty-puppet-hostie/pom.xml b/wechaty-puppet-hostie/pom.xml index c901eb3..9742318 100644 --- a/wechaty-puppet-hostie/pom.xml +++ b/wechaty-puppet-hostie/pom.xml @@ -4,7 +4,7 @@ io.github.wechaty wechaty-parent - 0.1.3-SNAPSHOT + 0.1.5-SNAPSHOT 4.0.0 wechaty-puppet-hostie diff --git a/wechaty-puppet-hostie/src/main/kotlin/io/github/wechaty/grpc/GrpcPuppet.kt b/wechaty-puppet-hostie/src/main/kotlin/io/github/wechaty/grpc/GrpcPuppet.kt index 1e819c7..7e61a7a 100644 --- a/wechaty-puppet-hostie/src/main/kotlin/io/github/wechaty/grpc/GrpcPuppet.kt +++ b/wechaty-puppet-hostie/src/main/kotlin/io/github/wechaty/grpc/GrpcPuppet.kt @@ -35,27 +35,26 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { private val client: OkHttpClient = OkHttpClient() private var grpcClient: PuppetGrpc.PuppetBlockingStub? = null - private var grpcAsyncClient:PuppetGrpc.PuppetStub? = null + private var grpcAsyncClient: PuppetGrpc.PuppetStub? = null // private var finishLatch:CountDownLatch? = null - private fun discoverHostieIp():Pair{ + private fun discoverHostieIp(): Pair { val token = puppetOptions!!.token val request: Request = Request.Builder() - .url(CHATIE_ENDPOINT + token) - .build() + .url(CHATIE_ENDPOINT + token) + .build() - client.newCall(request).execute().use { - response -> + client.newCall(request).execute().use { response -> val string = response.body!!.string() val readValue = JsonUtils.readValue>(string) val ip = readValue["ip"] ?: error("cannot get ip by token, check token"); - val port = readValue["port"] ?:"8788" + val port = readValue["port"] ?: "8788" - return Pair(ip,port) + return Pair(ip, port) } } @@ -99,7 +98,7 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { try { if (logonoff()) { - emit(EventEnum.LOGOUT,EventLogoutPayload(getId()!!,"logout")) + emit(EventEnum.LOGOUT, EventLogoutPayload(getId()!!, "logout")) this.setId(null) } @@ -125,6 +124,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { return CompletableFuture.completedFuture(null) } + override fun setPuppetName() { + puppetOptions!!.name = "io.github.wechaty.grpc.GrpcPuppet" + } override fun unref() { @@ -135,15 +137,15 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { private fun startGrpcClient(): Future { val endPoint = puppetOptions?.endPoint - val discoverHostieIp:Pair + val discoverHostieIp: Pair discoverHostieIp = if (StringUtils.isEmpty(endPoint)) { discoverHostieIp() - }else{ + } else { val split = StringUtils.split(endPoint, ":") - if(split.size == 1) { - Pair(split[0],"8788") - }else{ - Pair(split[0],split[1]) + if (split.size == 1) { + Pair(split[0], "8788") + } else { + Pair(split[0], split[1]) } } @@ -153,7 +155,7 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { exitProcess(1) } val newFixedThreadPool = newFixedThreadPool(16) - channel = ManagedChannelBuilder.forAddress(discoverHostieIp.first,NumberUtils.toInt(discoverHostieIp.second)).usePlaintext().executor(newFixedThreadPool).build() + channel = ManagedChannelBuilder.forAddress(discoverHostieIp.first, NumberUtils.toInt(discoverHostieIp.second)).usePlaintext().executor(newFixedThreadPool).build() grpcClient = PuppetGrpc.newBlockingStub(channel) grpcAsyncClient = PuppetGrpc.newStub(channel) @@ -161,15 +163,15 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { } private fun startGrpcStream() { - val streamObserver = object :StreamObserver{ + val streamObserver = object : StreamObserver { override fun onNext(event: Event.EventResponse?) { onGrpcStreamEvent(event!!) } override fun onError(t: Throwable?) { - log.error("error of grpc",t) - val payload = EventResetPayload(t?.message ?:"") - emit(EventEnum.RESET,payload) + log.error("error of grpc", t) + val payload = EventResetPayload(t?.message ?: "") + emit(EventEnum.RESET, payload) } override fun onCompleted() { @@ -179,7 +181,7 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { } val request = Event.EventRequest.newBuilder().build() - grpcAsyncClient!!.event(request,streamObserver) + grpcAsyncClient!!.event(request, streamObserver) } @@ -202,15 +204,15 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { } catch (e: Exception) { log.error("logout() rejection: %s", e) } finally { - emit(EventEnum.LOGOUT,EventLogoutPayload(getId()!!,"logout")) + emit(EventEnum.LOGOUT, EventLogoutPayload(getId()!!, "logout")) } return CompletableFuture.completedFuture(null) } override fun ding(data: String?) { val request = Base.DingRequest.newBuilder() - .setData(data) - .build() + .setData(data) + .build() CompletableFuture.runAsync { try { @@ -223,8 +225,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun contactSelfName(name: String): Future { val request = Contact.ContactSelfNameRequest.newBuilder() - .setName(name) - .build() + .setName(name) + .build() return CompletableFuture.supplyAsync { grpcClient!!.contactSelfName(request) null @@ -244,8 +246,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun contactSelfSignature(signature: String): Future { val request = Contact.ContactSelfSignatureRequest.newBuilder() - .setSignature(signature) - .build() + .setSignature(signature) + .build() return CompletableFuture.supplyAsync { grpcClient!!.contactSelfSignature(request) @@ -256,9 +258,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun tagContactAdd(tagId: String, contactId: String): Future { val request = Tag.TagContactAddRequest.newBuilder() - .setId(tagId) - .setContactId(contactId) - .build() + .setId(tagId) + .setContactId(contactId) + .build() return CompletableFuture.supplyAsync { grpcClient!!.tagContactAdd(request) @@ -270,8 +272,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun tagContactDelete(tagId: String): Future { val request = Tag.TagContactDeleteRequest.newBuilder() - .setId(tagId) - .build() + .setId(tagId) + .build() return CompletableFuture.supplyAsync { grpcClient!!.tagContactDelete(request) return@supplyAsync null @@ -281,14 +283,14 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun tagContactList(contactId: String): Future> { val stringValue = StringValue.newBuilder() - .setValue(contactId) - .build() + .setValue(contactId) + .build() return CompletableFuture.supplyAsync { val request = Tag.TagContactListRequest.newBuilder() - .setContactId(stringValue) - .build() + .setContactId(stringValue) + .build() val contactList = grpcClient!!.tagContactList(request) contactList.idsList } @@ -306,9 +308,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun tagContactRemove(tagId: String, contactId: String): Future { val request = Tag.TagContactRemoveRequest.newBuilder() - .setId(tagId) - .setContactId(contactId) - .build() + .setId(tagId) + .setContactId(contactId) + .build() return CompletableFuture.supplyAsync { grpcClient!!.tagContactRemove(request) @@ -322,8 +324,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { return CompletableFuture.supplyAsync { val request = Contact.ContactAliasRequest.newBuilder() - .setId(contactId) - .build() + .setId(contactId) + .build() val response = grpcClient!!.contactAlias(request) val alias = response.alias return@supplyAsync alias.value @@ -334,9 +336,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { val stringValue = StringValue.newBuilder().setValue(alias).build() val request = Contact.ContactAliasRequest.newBuilder() - .setId(contactId) - .setAlias(stringValue) - .build() + .setId(contactId) + .setAlias(stringValue) + .build() return CompletableFuture.supplyAsync() { grpcClient!!.contactAlias(request) @@ -347,8 +349,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun getContactAvatar(contactId: String): Future { val request = Contact.ContactAvatarRequest.newBuilder() - .setId(contactId) - .build() + .setId(contactId) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.contactAvatar(request) @@ -365,9 +367,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { val value = StringValue.newBuilder().setValue(toJsonString) val request = Contact.ContactAvatarRequest.newBuilder() - .setId(contactId) - .setFilebox(value) - .build() + .setId(contactId) + .setFilebox(value) + .build() return CompletableFuture.supplyAsync { grpcClient!!.contactAvatar(request) @@ -389,8 +391,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun contactRawPayload(contractId: String): Future { val request = Contact.ContactPayloadRequest.newBuilder() - .setId(contractId) - .build() + .setId(contractId) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.contactPayload(request) @@ -400,7 +402,7 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { payload.avatar = response.avatar payload.city = response.city payload.friend = response.friend - payload.gender = ContractGender.getByCode(response.gender.number) + payload.gender = ContactGender.getByCode(response.gender.number) payload.name = response.name payload.province = response.province payload.signature = response.signature @@ -419,8 +421,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun friendshipAccept(friendshipId: String): Future { val request = Friendship.FriendshipAcceptRequest.newBuilder() - .setId(friendshipId) - .build() + .setId(friendshipId) + .build() return CompletableFuture.supplyAsync { grpcClient!!.friendshipAccept(request) @@ -431,9 +433,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun friendshipAdd(contractId: String, hello: String): Future { val request = Friendship.FriendshipAddRequest.newBuilder() - .setContactId(contractId) - .setHello(hello) - .build() + .setContactId(contractId) + .setHello(hello) + .build() return CompletableFuture.supplyAsync { grpcClient!!.friendshipAdd(request) @@ -445,8 +447,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun friendshipSearchPhone(phone: String): Future { val request = Friendship.FriendshipSearchPhoneRequest.newBuilder() - .setPhone(phone) - .build() + .setPhone(phone) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.friendshipSearchPhone(request) @@ -456,8 +458,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun friendshipSearchWeixin(weixin: String): Future { val request = Friendship.FriendshipSearchWeixinRequest.newBuilder() - .setWeixin(weixin) - .build() + .setWeixin(weixin) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.friendshipSearchWeixin(request) @@ -468,8 +470,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun friendshipRawPayload(friendshipId: String): Future { val request = Friendship.FriendshipPayloadRequest.newBuilder() - .setId(friendshipId) - .build() + .setId(friendshipId) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.friendshipPayload(request) @@ -493,8 +495,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun messageContact(messageId: String): Future { val request = Message.MessageContactRequest.newBuilder() - .setId(messageId) - .build() + .setId(messageId) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.messageContact(request) response.id @@ -504,8 +506,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun messageFile(messageId: String): Future { val request = Message.MessageFileRequest.newBuilder() - .setId(messageId) - .build() + .setId(messageId) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.messageFile(request) @@ -519,9 +521,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { val imageType1 = Message.ImageType.forNumber(imageType.code) val request = Message.MessageImageRequest.newBuilder() - .setId(messageId) - .setType(imageType1) - .build() + .setId(messageId) + .setType(imageType1) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.messageImage(request) @@ -534,8 +536,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun messageMiniProgram(messageId: String): Future { val request = Message.MessageMiniProgramRequest.newBuilder() - .setId(messageId) - .build() + .setId(messageId) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.messageMiniProgram(request) @@ -548,8 +550,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun messageUrl(messageId: String): Future { val request = Message.MessageUrlRequest.newBuilder() - .setId(messageId) - .build() + .setId(messageId) + .build() return CompletableFuture.supplyAsync { @@ -564,9 +566,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun messageSendContact(conversationId: String, contactId: String): Future { val request = Message.MessageSendContactRequest.newBuilder() - .setContactId(contactId) - .setConversationId(conversationId) - .build() + .setContactId(contactId) + .setConversationId(conversationId) + .build() return CompletableFuture.supplyAsync { @@ -583,9 +585,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { log.debug("json size is {}", fileJson.length) val request = Message.MessageSendFileRequest.newBuilder() - .setConversationId(conversationId) - .setFilebox(fileJson) - .build() + .setConversationId(conversationId) + .setFilebox(fileJson) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.messageSendFile(request) @@ -597,9 +599,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun messageSendMiniProgram(conversationId: String, miniProgramPayload: MiniProgramPayload): Future { val request = Message.MessageSendMiniProgramRequest.newBuilder() - .setConversationId(conversationId) - .setMiniProgram(JsonUtils.write(miniProgramPayload)) - .build() + .setConversationId(conversationId) + .setMiniProgram(JsonUtils.write(miniProgramPayload)) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.messageSendMiniProgram(request) @@ -610,9 +612,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun messageSendText(conversationId: String, text: String, mentionList: List?): Future { val request = Message.MessageSendTextRequest.newBuilder() - .setConversationId(conversationId) - .setText(text) - .build() + .setConversationId(conversationId) + .setText(text) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.messageSendText(request) @@ -624,9 +626,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun messageSendUrl(conversationId: String, urlLinkPayload: UrlLinkPayload): Future { val request = Message.MessageSendUrlRequest.newBuilder() - .setConversationId(conversationId) - .setUrlLink(JsonUtils.write(urlLinkPayload)) - .build() + .setConversationId(conversationId) + .setUrlLink(JsonUtils.write(urlLinkPayload)) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.messageSendUrl(request) @@ -638,8 +640,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun messageRecall(messageId: String): Future { val request = Message.MessageRecallRequest.newBuilder() - .setId(messageId) - .build() + .setId(messageId) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.messageRecall(request) @@ -674,8 +676,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun roomInvitationAccept(roomInvitation: String): Future { val request = RoomInvitation.RoomInvitationAcceptRequest.newBuilder() - .setId(roomInvitation) - .build() + .setId(roomInvitation) + .build() return CompletableFuture.supplyAsync { grpcClient!!.roomInvitationAccept(request) @@ -686,8 +688,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun roomInvitationRawPayload(roomInvitationId: String): Future { val request = RoomInvitation.RoomInvitationPayloadRequest.newBuilder() - .setId(roomInvitationId) - .build() + .setId(roomInvitationId) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.roomInvitationPayload(request) @@ -716,9 +718,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun roomAdd(roomId: String, contactId: String): Future { val request = Room.RoomAddRequest.newBuilder() - .setContactId(contactId) - .setId(roomId) - .build() + .setContactId(contactId) + .setId(roomId) + .build() return CompletableFuture.supplyAsync { grpcClient!!.roomAdd(request) @@ -730,8 +732,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun roomAvatar(roomId: String): Future { val request = Room.RoomAvatarRequest.newBuilder() - .setId(roomId) - .build() + .setId(roomId) + .build() return CompletableFuture.supplyAsync { @@ -747,9 +749,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun roomCreate(contactIdList: List, topic: String?): Future { val request = Room.RoomCreateRequest.newBuilder() - .setTopic(topic) - .addAllContactIds(contactIdList) - .build() + .setTopic(topic) + .addAllContactIds(contactIdList) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.roomCreate(request) @@ -762,9 +764,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun roomDel(roomId: String, contactId: String): Future { val request = Room.RoomDelRequest.newBuilder() - .setId(roomId) - .setContactId(contactId) - .build() + .setId(roomId) + .setContactId(contactId) + .build() return CompletableFuture.supplyAsync { grpcClient!!.roomDel(request) @@ -787,8 +789,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun roomQRCode(roomId: String): Future { val request = Room.RoomQRCodeRequest.newBuilder() - .setId(roomId) - .build() + .setId(roomId) + .build() return CompletableFuture.supplyAsync { @@ -801,8 +803,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun roomQuit(roomId: String): Future { val request = Room.RoomQuitRequest.newBuilder() - .setId(roomId) - .build() + .setId(roomId) + .build() return CompletableFuture.supplyAsync { grpcClient!!.roomQuit(request) @@ -814,8 +816,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun roomTopic(roomId: String): Future? { val request = Room.RoomTopicRequest.newBuilder() - .setId(roomId) - .build() + .setId(roomId) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.roomTopic(request) @@ -830,9 +832,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { val value = StringValue.newBuilder().setValue(topic) val request = Room.RoomTopicRequest.newBuilder() - .setId(roomId) - .setTopic(value) - .build() + .setId(roomId) + .setTopic(value) + .build() return CompletableFuture.supplyAsync { grpcClient!!.roomTopic(request) @@ -843,8 +845,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun roomRawPayload(roomId: String): Future { val request = Room.RoomPayloadRequest.newBuilder() - .setId(roomId) - .build() + .setId(roomId) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.roomPayload(request) @@ -867,8 +869,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun getRoomAnnounce(roomId: String): Future { val request = Room.RoomAnnounceRequest.newBuilder() - .setId(roomId) - .build() + .setId(roomId) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.roomAnnounce(request) @@ -883,9 +885,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { val value = StringValue.newBuilder().setValue(text) val request = Room.RoomAnnounceRequest.newBuilder() - .setId(roomId) - .setText(value) - .build() + .setId(roomId) + .setText(value) + .build() return CompletableFuture.supplyAsync { grpcClient!!.roomAnnounce(request) @@ -898,8 +900,8 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun roomMemberList(roomId: String): Future> { val request = RoomMember.RoomMemberListRequest.newBuilder() - .setId(roomId) - .build() + .setId(roomId) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.roomMemberList(request) @@ -912,9 +914,9 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { override fun roomMemberRawPayload(roomId: String, contactId: String): Future { val request = RoomMember.RoomMemberPayloadRequest.newBuilder() - .setId(roomId) - .setMemberId(contactId) - .build() + .setId(roomId) + .setMemberId(contactId) + .build() return CompletableFuture.supplyAsync { val response = grpcClient!!.roomMemberPayload(request) @@ -944,7 +946,7 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { log.debug("PuppetHostie $type payload $payload") if (type != Event.EventType.EVENT_TYPE_HEARTBEAT) { - emit(EventEnum.HEART_BEAT, EventHeartbeatPayload("heartbeat")) + emit(EventEnum.HEART_BEAT, EventHeartbeatPayload("heartbeat",6000)) } when (type) { @@ -1029,7 +1031,6 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { } - companion object { private val log = LoggerFactory.getLogger(GrpcPuppet::class.java) } diff --git a/wechaty-puppet-mock/pom.xml b/wechaty-puppet-mock/pom.xml index 815109c..6d7c58a 100644 --- a/wechaty-puppet-mock/pom.xml +++ b/wechaty-puppet-mock/pom.xml @@ -5,7 +5,7 @@ wechaty-parent io.github.wechaty - 0.1.3-SNAPSHOT + 0.1.5-SNAPSHOT 4.0.0 diff --git a/wechaty-puppet-mock/src/main/kotlin/io/github/wechaty/MockData.kt b/wechaty-puppet-mock/src/main/kotlin/io/github/wechaty/MockData.kt index 32c2873..c563cd9 100644 --- a/wechaty-puppet-mock/src/main/kotlin/io/github/wechaty/MockData.kt +++ b/wechaty-puppet-mock/src/main/kotlin/io/github/wechaty/MockData.kt @@ -21,7 +21,7 @@ class MockData { contactPayload.avatar = faker.avatar().toString() contactPayload.city = faker.address().city() contactPayload.friend = true - contactPayload.gender = ContractGender.Male + contactPayload.gender = ContactGender.Male contactPayload.name = faker.name().firstName() contactPayload.province = faker.address().state() contactPayload.signature = faker.lorem().sentence() diff --git a/wechaty-puppet-mock/src/main/kotlin/io/github/wechaty/MockPuppet.kt b/wechaty-puppet-mock/src/main/kotlin/io/github/wechaty/MockPuppet.kt index 2f6a4a1..9e47bbb 100644 --- a/wechaty-puppet-mock/src/main/kotlin/io/github/wechaty/MockPuppet.kt +++ b/wechaty-puppet-mock/src/main/kotlin/io/github/wechaty/MockPuppet.kt @@ -3,6 +3,7 @@ package io.github.wechaty import io.github.wechaty.filebox.FileBox import io.github.wechaty.io.github.wechaty.schemas.EventEnum import io.github.wechaty.schemas.* +import io.github.wechaty.utils.JsonUtils import org.slf4j.LoggerFactory import java.util.* import java.util.concurrent.CompletableFuture @@ -71,6 +72,10 @@ class MockPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { return CompletableFuture.completedFuture(null) } + override fun setPuppetName() { + puppetOptions!!.name = "io.github.wechaty.MockPuppet" + } + override fun logout(): Future { log.info("MockPuppet logout()") val id = getId() ?: throw Exception("logout before login?") @@ -132,181 +137,275 @@ class MockPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) { } override fun contactAlias(contactId: String, alias: String?): Future { - log.info("MockPuppet contactAlias($contactId,$alias)") + log.info("MockPuppet getContactAvatar($contactId,$alias)") return CompletableFuture.completedFuture(null) } override fun getContactAvatar(contactId: String): Future { - log.info("MockPuppet contactAvatar($contactId)") - TODO("Not yet implemented") + log.info("MockPuppet getContactAvatar($contactId)") + return CompletableFuture.completedFuture(FileBox.fromFile("image/mock.png", "mock.png")) } override fun setContactAvatar(contactId: String, file: FileBox): Future { - TODO("Not yet implemented") + return CompletableFuture.completedFuture(null) } override fun contactList(): Future> { - TODO("Not yet implemented") + log.info("MockPuppet contactList()") + + return CompletableFuture.completedFuture(listOf()) } override fun contactRawPayload(contractId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet contactRawPayload($contractId)") + val contactPayload = ContactPayload(contractId) + contactPayload.name = "mock name" + return CompletableFuture.completedFuture(contactPayload) } override fun contactRawPayloadParser(rawPayload: ContactPayload): Future { - TODO("Not yet implemented") + log.info("MockPuppet contactRawPayloadParser($rawPayload)") + val contactPayload = ContactPayload(rawPayload.id) + contactPayload.avatar = "mock-avatar-data" + contactPayload.gender = ContactGender.Unknown + contactPayload.name = rawPayload.name + contactPayload.type = ContactType.Unknown + return CompletableFuture.completedFuture(contactPayload) } override fun friendshipAccept(friendshipId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet friendshipAccept($friendshipId)") + + return CompletableFuture.completedFuture(null) } override fun friendshipAdd(contractId: String, hello: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet friendshipAdd($contractId,$hello)") + + return CompletableFuture.completedFuture(null) } override fun friendshipSearchPhone(phone: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet friendshipSearchPhone($phone)") + + return CompletableFuture.completedFuture(null) } override fun friendshipSearchWeixin(weixin: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet friendshipSearchWeixin($weixin)") + + return CompletableFuture.completedFuture(null) } override fun friendshipRawPayload(friendshipId: String): Future { - TODO("Not yet implemented") + val friendshipPayload = FriendshipPayload() + friendshipPayload.id = friendshipId + return CompletableFuture.completedFuture(friendshipPayload) } override fun friendshipRawPayloadParser(rawPayload: FriendshipPayload): Future { - TODO("Not yet implemented") + return CompletableFuture.completedFuture(rawPayload) } override fun messageContact(messageId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet messageContact($messageId)") + return CompletableFuture.completedFuture("fake-id") } override fun messageFile(messageId: String): Future { - TODO("Not yet implemented") + return CompletableFuture.completedFuture(FileBox.fromBase64("cRH9qeL3XyVnaXJkppBuH20tf5JlcG9uFX1lL2IvdHRRRS9kMMQxOPLKNYIzQQ==", "mock-file$messageId.txt")) } override fun messageImage(messageId: String, imageType: ImageType): Future { - TODO("Not yet implemented") + log.info("MockPuppet messageImage($messageId,$imageType)") + return CompletableFuture.completedFuture(FileBox.fromQRCode("fake-qrcode")) } override fun messageMiniProgram(messageId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet messageMiniProgram($messageId)") + val miniProgramPayload = MiniProgramPayload() + miniProgramPayload.title = "mock title for $messageId" + return CompletableFuture.completedFuture(miniProgramPayload) } override fun messageUrl(messageId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet messageUrl($messageId)") + return CompletableFuture.completedFuture(UrlLinkPayload("mock title for $messageId", "https://mock.url")) } override fun messageSendContact(conversationId: String, contactId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet messageSendContact($conversationId,$contactId)") + + return CompletableFuture.completedFuture(null) } override fun messageSendFile(conversationId: String, file: FileBox): Future { - TODO("Not yet implemented") + log.info("MockPuppet messageSendFile($conversationId,$file)") + + return CompletableFuture.completedFuture(null) } override fun messageSendMiniProgram(conversationId: String, miniProgramPayload: MiniProgramPayload): Future { - TODO("Not yet implemented") + log.info("MockPuppet messageSendMiniProgram($conversationId,${JsonUtils.write(miniProgramPayload)})") + + return CompletableFuture.completedFuture(null) } override fun messageSendText(conversationId: String, text: String, mentionList: List?): Future { - TODO("Not yet implemented") + log.info("MockPuppet messageSendText($conversationId,$text,${JsonUtils.write(mentionList ?: "")})") + + return CompletableFuture.completedFuture("mock-msg-$conversationId") } override fun messageSendUrl(conversationId: String, urlLinkPayload: UrlLinkPayload): Future { - TODO("Not yet implemented") + log.info("MockPuppet messageSendUrl($conversationId,${JsonUtils.write(urlLinkPayload)})") + + return CompletableFuture.completedFuture(null) } override fun messageRecall(messageId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet messageRecall($messageId)") + + return CompletableFuture.completedFuture(false) } override fun messageRawPayload(messageId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet messageRawPayload($messageId)") + val messagePayload = MessagePayload(messageId) + messagePayload.fromId = "from_id" + messagePayload.text = "mock message text" + messagePayload.toId = "to_id" + return CompletableFuture.completedFuture(messagePayload) } override fun messageRawPayloadParser(rawPayload: MessagePayload): Future { - TODO("Not yet implemented") + log.info("MockPuppet messageRawPayloadParser($rawPayload)") + val messagePayload = MessagePayload(rawPayload.id) + messagePayload.fromId = rawPayload.fromId + messagePayload.mentionIdList = listOf() + messagePayload.text = rawPayload.text + messagePayload.timestamp = Date().time + messagePayload.toId = rawPayload.toId + messagePayload.type = MessageType.Text + return CompletableFuture.completedFuture(messagePayload) } override fun roomInvitationAccept(roomInvitation: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet roomInvitationAccept($roomInvitation)") + + return CompletableFuture.completedFuture(null) } override fun roomInvitationRawPayload(roomInvitationId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet roomInvitationRawPayload($roomInvitationId)") + + return CompletableFuture.completedFuture(null) } override fun roomInvitationRawPayloadParser(rawPayload: RoomInvitationPayload): Future { - TODO("Not yet implemented") + log.info("MockPuppet rromInvitationRawPayloadParser(${JsonUtils.write(rawPayload)})") + return CompletableFuture.completedFuture(rawPayload) } override fun roomAdd(roomId: String, contactId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet roomAdd($roomId,$contactId)") + return CompletableFuture.completedFuture(null) } override fun roomAvatar(roomId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet roomAvatar($roomId)") + + val roomPayload = this.roomPayload(roomId).get() + if (roomPayload.avatar != null) { + return CompletableFuture.completedFuture(FileBox.fromUrl(roomPayload.avatar!!, "room-avatar")) + } + log.warn("MockPuppet roomAvatar() avatar not found,use the chatie default.") + return CompletableFuture.completedFuture(qrCodeForChatie()) } override fun roomCreate(contactIdList: List, topic: String?): Future { - TODO("Not yet implemented") + log.info("MockPuppet roomCreate($contactIdList,$topic)") + return CompletableFuture.completedFuture("mock_room_id") } override fun roomDel(roomId: String, contactId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet roomDel($roomId,$contactId)") + return CompletableFuture.completedFuture(null) } override fun roomList(): Future> { - TODO("Not yet implemented") + log.info("MockPuppet roomList()") + + return CompletableFuture.completedFuture(listOf()) } override fun roomQRCode(roomId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet roomQRCode($roomId)") + return CompletableFuture.completedFuture("$roomId mock qrcode") } override fun roomQuit(roomId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet roomQuit($roomId)") + return CompletableFuture.completedFuture(null) } override fun roomTopic(roomId: String): Future? { - TODO("Not yet implemented") + log.info("MockPuppet roomTopic($roomId)") + return CompletableFuture.completedFuture("mock room topic") } override fun roomTopic(roomId: String, topic: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet roomTopic($roomId,$topic)") + return CompletableFuture.completedFuture(null) } override fun roomRawPayload(roomId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet roomRawPayload($roomId)") + val roomPayload = RoomPayload(roomId) + roomPayload.memberIdList = listOf() + roomPayload.ownerId = "mock_room_owner_id" + roomPayload.topic = "mock topic" + return CompletableFuture.completedFuture(roomPayload) } override fun roomRawPayloadParser(roomPayload: RoomPayload): Future { - TODO("Not yet implemented") + log.info("MockPuppet roomRawPayloadParser(${JsonUtils.write(roomPayload)})") + val payload = RoomPayload(roomPayload.id) + payload.ownerId = roomPayload.id + payload.adminIdList = listOf() + payload.memberIdList = listOf() + payload.topic = "mock topic" + return CompletableFuture.completedFuture(payload) } override fun getRoomAnnounce(roomId: String): Future { - TODO("Not yet implemented") + return CompletableFuture.completedFuture("mock announcement for $roomId") } override fun setRoomAnnounce(roomId: String, text: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet setRoomAnnounce($roomId,$text)") + return CompletableFuture.completedFuture(null) } override fun roomMemberList(roomId: String): Future> { - TODO("Not yet implemented") + log.info("MockPuppet roomMemberList($roomId)") + return CompletableFuture.completedFuture(listOf()) } override fun roomMemberRawPayload(roomId: String, contactId: String): Future { - TODO("Not yet implemented") + log.info("MockPuppet roomMemberRawPayload($roomId,$contactId)") + val roomMemberPayload = RoomMemberPayload() + roomMemberPayload.avatar = "mock-avatar-data" + roomMemberPayload.id = "xx" + roomMemberPayload.name = "mock-name" + roomMemberPayload.roomAlias = "yy" + + return CompletableFuture.completedFuture(roomMemberPayload) } override fun roomMemberRawPayloadParser(rawPayload: RoomMemberPayload): Future { - TODO("Not yet implemented") + log.info("MockPuppet roomMemberRawPayloadParser(${JsonUtils.write(rawPayload)})") + return CompletableFuture.completedFuture(rawPayload) } companion object { diff --git a/wechaty-puppet-mock/src/main/resources/image/mock.png b/wechaty-puppet-mock/src/main/resources/image/mock.png new file mode 100644 index 0000000..c66f597 Binary files /dev/null and b/wechaty-puppet-mock/src/main/resources/image/mock.png differ diff --git a/wechaty-puppet/pom.xml b/wechaty-puppet/pom.xml index 1a4508f..4279c40 100644 --- a/wechaty-puppet/pom.xml +++ b/wechaty-puppet/pom.xml @@ -4,7 +4,7 @@ io.github.wechaty wechaty-parent - 0.1.3-SNAPSHOT + 0.1.5-SNAPSHOT 4.0.0 wechaty-puppet @@ -104,7 +104,20 @@ com.squareup.okhttp3 okhttp - + + org.hamcrest + hamcrest-core + + + org.mockito + mockito-core + test + + + junit + junit + test + diff --git a/wechaty-puppet/src/main/kotlin/Puppet.kt b/wechaty-puppet/src/main/kotlin/Puppet.kt index c3ab433..a91cdc1 100644 --- a/wechaty-puppet/src/main/kotlin/Puppet.kt +++ b/wechaty-puppet/src/main/kotlin/Puppet.kt @@ -98,8 +98,8 @@ abstract class Puppet : EventEmitter { on(EventEnum.RESET, object : PuppetResetListener { override fun handler(payload: EventResetPayload) { - log.debug("get a reset message") - if(rateLimiter.tryAcquire()){ + log.debug("get a reset message") + if (rateLimiter.tryAcquire()) { reset(payload.data) } @@ -155,7 +155,7 @@ abstract class Puppet : EventEmitter { override fun handler(vararg any: Any) { - log.debug("class Type is {}",any[0].javaClass.name) + log.debug("class Type is {}", any[0].javaClass.name) listener.handler(any[0] as EventDongPayload) @@ -226,7 +226,7 @@ abstract class Puppet : EventEmitter { override fun handler(vararg any: Any) { - log.debug("class Type is {}",any[0].javaClass.name) + log.debug("class Type is {}", any[0].javaClass.name) listener.handler(any[0] as EventScanPayload) @@ -264,7 +264,7 @@ abstract class Puppet : EventEmitter { override fun handler(vararg any: Any) { - log.debug("class Type is {}",any[0].javaClass.name) + log.debug("class Type is {}", any[0].javaClass.name) listener.handler(any[0] as EventHeartbeatPayload) @@ -291,6 +291,7 @@ abstract class Puppet : EventEmitter { abstract fun start(): Future abstract fun stop(): Future + abstract fun setPuppetName() open fun unref() { } @@ -348,6 +349,7 @@ abstract class Puppet : EventEmitter { * contactSelf */ abstract fun contactSelfName(name: String): Future + abstract fun contactSelfQRCode(): Future abstract fun contactSelfSignature(signature: String): Future @@ -362,6 +364,7 @@ abstract class Puppet : EventEmitter { * */ abstract fun tagContactAdd(tagId: String, contactId: String): Future + abstract fun tagContactDelete(tagId: String): Future abstract fun tagContactList(contactId: String): Future> abstract fun tagContactList(): Future> @@ -373,6 +376,7 @@ abstract class Puppet : EventEmitter { * */ abstract fun contactAlias(contactId: String): Future + abstract fun contactAlias(contactId: String, alias: String?): Future abstract fun getContactAvatar(contactId: String): Future abstract fun setContactAvatar(contactId: String, file: FileBox): Future @@ -422,39 +426,39 @@ abstract class Puppet : EventEmitter { return@supplyAsync list } - val stream = list?.stream()?.map{contactPayload(it).get()} - if(StringUtils.isNotBlank(query.name)){ - stream?.filter { + var stream = list?.stream()?.map { contactPayload(it).get() } + if (StringUtils.isNotBlank(query.name)) { + stream = stream?.filter { StringUtils.equals(query.name, it.name) } } - if(StringUtils.isNotBlank(query.alias)){ - stream?.filter { + if (StringUtils.isNotBlank(query.alias)) { + stream = stream?.filter { StringUtils.equals(query.alias, it.alias) } } - if(StringUtils.isNotBlank(query.id)){ - stream?.filter { + if (StringUtils.isNotBlank(query.id)) { + stream = stream?.filter { StringUtils.equals(query.alias, it.alias) } } - if(StringUtils.isNotBlank(query.weixin)){ - stream?.filter { + if (StringUtils.isNotBlank(query.weixin)) { + stream = stream?.filter { StringUtils.equals(query.alias, it.alias) } } - if(query.nameReg != null){ - stream?.filter{ + if (query.nameReg != null) { + stream = stream?.filter { query.nameReg!!.matches(it.name ?: "") } } - if(query.aliasReg != null){ - stream?.filter{ + if (query.aliasReg != null) { + stream = stream?.filter { query.aliasReg!!.matches(it.alias ?: "") } } @@ -468,7 +472,7 @@ abstract class Puppet : EventEmitter { } } - fun ContactPayloadFilterFactory(query:ContactQueryFilter):ContactPayloadFilterFunction{ + fun ContactPayloadFilterFactory(query: ContactQueryFilter): ContactPayloadFilterFunction { val clz = query::class.java val fields = clz.fields @@ -490,13 +494,11 @@ abstract class Puppet : EventEmitter { } - - protected fun contactPayloadCache(contactId: String): ContactPayload? { val contactPayload = cacheContactPayload.getIfPresent(contactId) - log.debug("contactPayload is {} by id {}", contactPayload,contactId) + log.debug("contactPayload is {} by id {}", contactPayload, contactId) return contactPayload } @@ -529,6 +531,7 @@ abstract class Puppet : EventEmitter { * */ abstract fun friendshipAccept(friendshipId: String): Future + abstract fun friendshipAdd(contractId: String, hello: String): Future abstract fun friendshipSearchPhone(phone: String): Future abstract fun friendshipSearchWeixin(weixin: String): Future @@ -594,6 +597,7 @@ abstract class Puppet : EventEmitter { */ abstract fun messageContact(messageId: String): Future + abstract fun messageFile(messageId: String): Future abstract fun messageImage(messageId: String, imageType: ImageType): Future abstract fun messageMiniProgram(messageId: String): Future @@ -646,46 +650,46 @@ abstract class Puppet : EventEmitter { messagePayload(it).get() } - val stream = messagePayloadList.stream() + var stream = messagePayloadList.stream() if (StringUtils.isNotEmpty(query.fromId)) { - stream.filter { + stream = stream.filter { StringUtils.equals(it.fromId, query.fromId) } } if (StringUtils.isNotEmpty(query.id)) { - stream.filter { + stream = stream.filter { StringUtils.equals(it.id, query.id) } } if (StringUtils.isNotEmpty(query.roomId)) { - stream.filter { + stream = stream.filter { StringUtils.equals(it.roomId, query.roomId) } } if (StringUtils.isNotEmpty(query.toId)) { - stream.filter { + stream = stream.filter { StringUtils.equals(it.toId, query.toId) } } if (StringUtils.isNotEmpty(query.text)) { - stream.filter { + stream = stream.filter { StringUtils.equals(it.text, query.text) } } if (query.textReg != null) { - stream.filter { + stream = stream.filter { query.textReg!!.matches(it.text ?: "") } } if (query.type != null) { - stream.filter { + stream = stream.filter { query.type == it.type } } @@ -790,6 +794,7 @@ abstract class Puppet : EventEmitter { * */ abstract fun roomAdd(roomId: String, contactId: String): Future + abstract fun roomAvatar(roomId: String): Future abstract fun roomCreate(contactIdList: List, topic: String?): Future @@ -809,6 +814,7 @@ abstract class Puppet : EventEmitter { */ abstract fun getRoomAnnounce(roomId: String): Future + abstract fun setRoomAnnounce(roomId: String, text: String): Future abstract fun roomMemberList(roomId: String): Future> diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/filebox/FileBox.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/filebox/FileBox.kt index 9c5482c..904e8ef 100644 --- a/wechaty-puppet/src/main/kotlin/io/github/wechaty/filebox/FileBox.kt +++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/filebox/FileBox.kt @@ -19,84 +19,88 @@ import java.util.concurrent.Future class FileBox(options: FileBoxOptions) { @JsonProperty - private var mimeType: String? = null + var mimeType: String? = null + @JsonProperty - private var base64 : String? = null + var base64: String? = null + @JsonProperty - private var remoteUrl : String? = null + var remoteUrl: String? = null + @JsonProperty - private var qrCode : String? = null + var qrCode: String? = null + @JsonProperty - private var buffer : ByteArray?=null - private var localPath : String? = null + var buffer: ByteArray? = null + var localPath: String? = null + @JsonProperty - private var headers : OutgoingHttpHeaders? = null + var headers: OutgoingHttpHeaders? = null + @JsonProperty - private var name :String? = null + var name: String? = null + @JsonProperty - private var metadata: Metadata? = null + var metadata: Metadata? = null + @JsonProperty - private lateinit var boxType:FileBoxType + var boxType: FileBoxType - private val client:OkHttpClient = OkHttpClient() + private val client: OkHttpClient = OkHttpClient() init { - when(options){ - is FileBoxOptionsBuffer ->{ + when (options) { + is FileBoxOptionsBuffer -> { this.name = options.name this.boxType = options.type this.buffer = options.buffer } - is FileBoxOptionsFile ->{ + is FileBoxOptionsFile -> { this.name = options.name this.boxType = options.type this.localPath = options.path } - is FileBoxOptionsUrl ->{ + is FileBoxOptionsUrl -> { this.name = options.name this.boxType = options.type this.remoteUrl = options.url this.headers = options.headers } - is FileBoxOptionsStream ->{ + is FileBoxOptionsStream -> { this.name = options.name this.boxType = options.type } - is FileBoxOptionsQRCode ->{ + is FileBoxOptionsQRCode -> { this.name = options.name this.boxType = options.type this.qrCode = options.qrCode } - is FileBoxOptionsBase64 ->{ + is FileBoxOptionsBase64 -> { this.name = options.name this.boxType = options.type this.base64 = options.base64 } - - - } - } - fun type():FileBoxType{ + fun type(): FileBoxType { return this.boxType } - fun ready():Future{ - if(this.boxType == FileBoxType.Url){ + fun ready(): Future { + if (this.boxType == FileBoxType.Url) { } return CompletableFuture.completedFuture(null); } - fun syncRemoteName():Future{ + fun syncRemoteName(): Future { val httpHeadHeader = httpHeadHeader(this.remoteUrl!!) @@ -121,54 +125,54 @@ class FileBox(options: FileBoxOptions) { } - private fun httpHeadHeader(url:String):Map>{ + private fun httpHeadHeader(url: String): Map> { val request: Request = Request.Builder() - .url(url) - .build() + .url(url) + .build() - client.newCall(request).execute().use { - response -> + client.newCall(request).execute().use { response -> val headers = response.headers return headers.toMultimap() } } - fun toJsonString():String { - buffer = toByte(this) + fun toJsonString(): String { + buffer = getBufferByte(this) return JsonUtils.write(this) } - fun toByte(fileBox: FileBox):ByteArray?{ - when(fileBox.type()){ - FileBoxType.File ->{ + private fun getBufferByte(fileBox: FileBox): ByteArray? { + when (fileBox.type()) { + FileBoxType.File -> { val file = File(ClassLoader.getSystemResource("dong.jpg").path) return FileUtils.readFileToByteArray(file) } - - FileBoxType.Url ->{ + FileBoxType.Url -> { return null; } - - else ->{ + FileBoxType.Base64 -> { + return null; + } + else -> { TODO() } } } - companion object{ + companion object { @JvmStatic - fun fromFile(path:String,name:String):FileBox{ + fun fromFile(path: String, name: String): FileBox { var localname = name - if(StringUtils.isEmpty(name)){ + if (StringUtils.isEmpty(name)) { localname = FilenameUtils.getBaseName(path) } @@ -178,70 +182,70 @@ class FileBox(options: FileBoxOptions) { } @JvmStatic - fun fromJson(obj:String):FileBox{ - - return JsonUtils.readValue(obj) - -// val jsonObject = JsonObject(obj) -// -// var fileBox:FileBox -// -// val type = jsonObject.getInteger("boxType") -// -// when(type){ -// -// FileBoxType.Base64.code ->{ -// fileBox = fromBase64( -// jsonObject.getString("base64"), -// jsonObject.getString("name") -// ) -// } -// -// FileBoxType.Url.code ->{ -// fileBox = fromUrl( -// jsonObject.getString("name"), -// jsonObject.getString("remoteUrl") -// ) -// } -// -// FileBoxType.QRcode.code ->{ -// fileBox = fromQRCode( -// jsonObject.getString("qrCode") -// ) -// } -// else ->{ -// throw Exception("unknown filebox json object{type} $jsonObject") -// } -// } -// -// fileBox.metadata = jsonObject.get("metadata") -// return fileBox; + fun fromJson(obj: String): FileBox { + +// return JsonUtils.readValue(obj) + + val jsonNode = JsonUtils.mapper.readTree(obj) + + var fileBox: FileBox + + val type = jsonNode.findValue("boxType").asInt() + + when (type) { + + FileBoxType.Base64.code -> { + fileBox = fromBase64( + jsonNode.findValue("base64").asText(), + jsonNode.findValue("name").asText() + ) + } + + FileBoxType.Url.code -> { + fileBox = fromUrl( + jsonNode.findValue("remoteUrl").asText(), + jsonNode.findValue("name").asText() + ) + } + + FileBoxType.QRcode.code -> { + fileBox = fromQRCode( + jsonNode.findValue("qrCode").asText() + ) + } + else -> { + throw Exception("unknown filebox json object{type} $jsonNode") + } + } + +// fileBox.metadata = jsonNode.get("metadata") + return fileBox; } @JvmStatic - fun fromBase64(base64: String,name:String):FileBox{ + fun fromBase64(base64: String, name: String): FileBox { val options = FileBoxOptionsBase64(base64 = base64, name = name) return FileBox(options) } @JvmStatic - fun fromDataUrl(dataUrl: String,name:String):FileBox{ + fun fromDataUrl(dataUrl: String, name: String): FileBox { val base64 = dataUrlToBase64(dataUrl); - return fromBase64(base64,name) + return fromBase64(base64, name) } @JvmStatic - fun fromQRCode(qrCode:String):FileBox{ + fun fromQRCode(qrCode: String): FileBox { val optionsQRCode = FileBoxOptionsQRCode(name = "qrcode.png", qrCode = qrCode) return FileBox(optionsQRCode) } @JvmStatic - fun fromUrl(url:String,name: String?,headers: OutgoingHttpHeaders? = null):FileBox{ + fun fromUrl(url: String, name: String?, headers: OutgoingHttpHeaders? = null): FileBox { - var localName :String? = name + var localName: String? = name - if(StringUtils.isEmpty(url)){ + if (StringUtils.isEmpty(url)) { val parsedUrl = URL(url) localName = parsedUrl.path } @@ -253,22 +257,13 @@ class FileBox(options: FileBoxOptions) { } - /** * ????? */ - fun dataUrlToBase64(dataUrl :String):String{ + fun dataUrlToBase64(dataUrl: String): String { val split = StringUtils.split(dataUrl, ",") return split[split.size - 1] } } - - - - - - - - } diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/Contact.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/Contact.kt index a67e320..e2725ee 100644 --- a/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/Contact.kt +++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/Contact.kt @@ -1,11 +1,11 @@ package io.github.wechaty.schemas -enum class ContractGender(var code: Int) { +enum class ContactGender(var code: Int) { Unknown(0), Male(1), Female(2); companion object { - fun getByCode(code: Int): ContractGender { + fun getByCode(code: Int): ContactGender { val values = values() for (value in values) { if (value.code == code) { @@ -46,7 +46,7 @@ class ContactQueryFilter { } class ContactPayload(val id:String) { - var gender: ContractGender? = null + var gender: ContactGender? = null var type: ContactType? = null var name: String? = null var avatar: String? = null diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/Event.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/Event.kt index ae8f72b..dbdf9dd 100644 --- a/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/Event.kt +++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/Event.kt @@ -135,7 +135,8 @@ data class EventResetPayload( } data class EventHeartbeatPayload( - var data:String + var data:String, + var timeout:Long ) { override fun toString(): String { return "EventHeartbeatPayload(data='$data')" diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/PuppetOptions.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/PuppetOptions.kt index 765780b..a607dc0 100644 --- a/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/PuppetOptions.kt +++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/PuppetOptions.kt @@ -5,5 +5,6 @@ class PuppetOptions { var timeout: Long? = null var token: String? = null var puppetOptionKey: String? = null + var name: String = "DEFAULT" } diff --git a/wechaty-puppet/src/test/kotlin/io/github/wechaty/filebox/FileBoxTest.kt b/wechaty-puppet/src/test/kotlin/io/github/wechaty/filebox/FileBoxTest.kt new file mode 100644 index 0000000..43935f3 --- /dev/null +++ b/wechaty-puppet/src/test/kotlin/io/github/wechaty/filebox/FileBoxTest.kt @@ -0,0 +1,21 @@ +package io.github.wechaty.filebox + +import junit.framework.Assert.assertEquals +import org.junit.Test + +const val EXPECTED_FILEBOX_URL = "http://testurl" +const val EXPECTED_FILEBOX_NAME = "fileboxname" + +class FileBoxTest { + + @Test + fun testFileBoxFromURLShallHaveCorrectNameAndURL() { + + var filebox : FileBox = FileBox.fromJson("{\"remoteUrl\":\"" + EXPECTED_FILEBOX_URL + "\"," + + "\"name\":\"" + EXPECTED_FILEBOX_NAME + "\"," + + "\"boxType\":2}") + + assertEquals(EXPECTED_FILEBOX_URL, filebox.remoteUrl) + assertEquals(EXPECTED_FILEBOX_NAME, filebox.name) + } +} diff --git a/wechaty/pom.xml b/wechaty/pom.xml index 9413fac..10aa138 100644 --- a/wechaty/pom.xml +++ b/wechaty/pom.xml @@ -4,7 +4,7 @@ io.github.wechaty wechaty-parent - 0.1.3-SNAPSHOT + 0.1.5-SNAPSHOT 4.0.0 wechaty @@ -63,20 +63,23 @@ - - - - - - - - + + org.apache.logging.log4j + log4j-core + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + - - - - + + com.lmax + disruptor + test + org.slf4j @@ -110,7 +113,10 @@ junit test - + + org.reflections + reflections + diff --git a/wechaty/src/main/kotlin/io/github/wechaty/Wechaty.kt b/wechaty/src/main/kotlin/io/github/wechaty/Wechaty.kt index f350069..10c9997 100644 --- a/wechaty/src/main/kotlin/io/github/wechaty/Wechaty.kt +++ b/wechaty/src/main/kotlin/io/github/wechaty/Wechaty.kt @@ -4,7 +4,6 @@ package io.github.wechaty; import io.github.wechaty.eventEmitter.Event import io.github.wechaty.eventEmitter.EventEmitter import io.github.wechaty.eventEmitter.Listener -import io.github.wechaty.grpc.GrpcPuppet import io.github.wechaty.io.github.wechaty.schemas.EventEnum import io.github.wechaty.listener.* //import io.github.wechaty.memorycard.MemoryCard @@ -38,14 +37,15 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) : val contactManager = ContactManager(this) val messageManager = MessageManager(this) val roomManager = RoomManager(this) - val roomInvitationMessage = RoomInvitationManager(this) + val roomInvitationManager = RoomInvitationManager(this) + val imageManager = ImageManager(this) + val friendshipManager = FriendshipManager(this) init { // this.memory = wechatyOptions.memory installGlobalPlugin() } - fun start(await: Boolean = false):Wechaty { initPuppet() @@ -72,6 +72,25 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) : puppet.stop() } + fun logout(){ + log.debug("Wechaty logout()") + try { + puppet.logout() + } catch (e: Exception) { + log.error("logout error",e) + throw e + } + } + + fun logonoff():Boolean{ + return try { + puppet.logonoff() + } catch (e: Exception) { + false + } + } + + fun name(): String { return wechatyOptions.name } @@ -172,15 +191,10 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) : } private fun initPuppet() { - this.puppet = GrpcPuppet(puppetOptions) + this.puppet = PuppetManager.resolveInstance(wechatyOptions).get() initPuppetEventBridge(puppet) } - fun friendship(): Friendship { - return Friendship(this); - } - - fun getPuppet(): Puppet { return puppet } @@ -191,6 +205,13 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) : return user } + fun say(any: Any):Message?{ + return userSelf().say(any) + } + + fun ding(data:String?){ + this.puppet.ding(data) + } private fun initPuppetEventBridge(puppet: Puppet) { @@ -226,7 +247,7 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) : EventEnum.FRIENDSHIP -> { puppet.on(it, object : PuppetFriendshipListener { override fun handler(payload: EventFriendshipPayload) { - val friendship = friendship().load(payload.friendshipId) + val friendship = friendshipManager.load(payload.friendshipId) friendship.ready() emit(EventEnum.FRIENDSHIP, friendship) } @@ -277,7 +298,7 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) : EventEnum.ROOM_INVITE -> { puppet.on(it, object : PuppetRoomInviteListener { override fun handler(payload: EventRoomInvitePayload) { - val roomInvitation = roomInvitationMessage.load(payload.roomInvitationId) + val roomInvitation = roomInvitationManager.load(payload.roomInvitationId) emit(EventEnum.ROOM_INVITE, roomInvitation) } }) @@ -355,6 +376,11 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) : } } + + override fun toString():String{ + return "wechaty" + } + companion object Factory { @JvmStatic fun instance(token: String): Wechaty { diff --git a/wechaty/src/main/kotlin/io/github/wechaty/WechatyOptions.kt b/wechaty/src/main/kotlin/io/github/wechaty/WechatyOptions.kt index 50c0b56..d930bf7 100644 --- a/wechaty/src/main/kotlin/io/github/wechaty/WechatyOptions.kt +++ b/wechaty/src/main/kotlin/io/github/wechaty/WechatyOptions.kt @@ -9,7 +9,8 @@ class WechatyOptions { var name:String = "Wechaty" - var puppet:String = "wechaty-puppet-hostie" +// var puppet:String = "wechaty-puppet-hostie" + var puppet:String = "io.github.wechaty.grpc.GrpcPuppet" var puppetOptions:PuppetOptions? = null diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/Contact.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/Contact.kt index e49fe43..8edd437 100644 --- a/wechaty/src/main/kotlin/io/github/wechaty/user/Contact.kt +++ b/wechaty/src/main/kotlin/io/github/wechaty/user/Contact.kt @@ -4,14 +4,17 @@ import io.github.wechaty.Accessory import io.github.wechaty.Puppet import io.github.wechaty.Wechaty import io.github.wechaty.filebox.FileBox +import io.github.wechaty.schemas.ContactGender import io.github.wechaty.schemas.ContactPayload import io.github.wechaty.schemas.ContactQueryFilter +import io.github.wechaty.schemas.ContactType import io.github.wechaty.type.Sayable import io.github.wechaty.utils.FutureUtils import org.apache.commons.lang3.StringUtils import org.slf4j.LoggerFactory import java.util.concurrent.CompletableFuture import java.util.concurrent.Future +import kotlin.math.E open class Contact(wechaty: Wechaty,val id:String) : Sayable, Accessory(wechaty) { @@ -114,8 +117,61 @@ open class Contact(wechaty: Wechaty,val id:String) : Sayable, Accessory(wechaty) return payload?.alias ?:null } - open fun avatar(): Future { - TODO() + fun stranger():Boolean?{ + return if(friend() == null){ + null + }else{ + !friend()!! + } + } + + fun friend():Boolean?{ + return payload?.friend + } + + fun type():ContactType{ + return payload?.type ?: throw Exception("no payload") + } + + fun gender():ContactGender{ + return payload?.gender ?: ContactGender.Unknown + } + + fun province():String?{ + return payload?.province + } + + fun city():String?{ + return payload?.city + } + + open fun avatar(): FileBox { + try { + return wechaty.getPuppet().getContactAvatar(this.id).get() + } catch (e: Exception) { + log.error("error",e) + TODO() + } + } + + fun tags():List{ + val tagIdList = wechaty.getPuppet().tagContactList(this.id).get() + return try { + tagIdList.map { + wechaty.tagManager.load(it) + } + } catch (e: Exception) { + log.error("error",e) + listOf() + } + } + + fun self():Boolean{ + val userId = puppet.selfId() + if(StringUtils.isEmpty(userId)){ + return false + } + return StringUtils.equals(id,userId) } diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/ContactSelf.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/ContactSelf.kt index 8183c77..bfb5d22 100644 --- a/wechaty/src/main/kotlin/io/github/wechaty/user/ContactSelf.kt +++ b/wechaty/src/main/kotlin/io/github/wechaty/user/ContactSelf.kt @@ -5,18 +5,27 @@ import io.github.wechaty.filebox.FileBox import java.util.concurrent.CompletableFuture import java.util.concurrent.Future -class ContactSelf(wechaty: Wechaty,id: String) : Contact(wechaty,id){ +class ContactSelf(wechaty: Wechaty, id: String) : Contact(wechaty, id) { - override fun avatar(): Future { - return super.avatar() + fun avatar(fileBox: FileBox) { + puppet.setContactAvatar(super.id, fileBox) } - fun avatar(fileBox:FileBox):Future{ - return CompletableFuture.supplyAsync { - puppet.setContactAvatar(super.id, fileBox) - null + fun setName(name:String){ + puppet.contactSelfName(name).get() + sync() + } + + fun signature(signature:String){ + + var puppetId:String? = puppet.selfId() + + let{ + puppetId != null + }.run { + puppet.contactSelfSignature(signature).get() + sync() } } - } diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/Friendship.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/Friendship.kt index 104e5f5..8afcb75 100644 --- a/wechaty/src/main/kotlin/io/github/wechaty/user/Friendship.kt +++ b/wechaty/src/main/kotlin/io/github/wechaty/user/Friendship.kt @@ -5,40 +5,15 @@ import io.github.wechaty.Wechaty import io.github.wechaty.schemas.FriendshipPayload import io.github.wechaty.schemas.FriendshipSearchCondition import io.github.wechaty.schemas.FriendshipType +import io.github.wechaty.utils.JsonUtils import org.apache.commons.lang3.StringUtils import org.slf4j.LoggerFactory -class Friendship (wechaty: Wechaty):Accessory(wechaty){ +class Friendship (wechaty: Wechaty,val id:String):Accessory(wechaty){ - constructor(wechaty: Wechaty,id: String):this(wechaty){ - this.id = id - } - - private var id:String? = null private var payload:FriendshipPayload? = null - fun load(id:String):Friendship{ - this.id = id - return this - } - - fun search(queryFilter: FriendshipSearchCondition):Contact?{ - val contactId = wechaty.getPuppet().friendshipSearch(queryFilter).get(); - if(StringUtils.isEmpty(contactId)){ - return null - } - val contact = wechaty.contactManager.load(contactId!!) - contact.ready() - return contact - } - - - fun add(contact: Contact, hello:String){ - log.debug("add contact: {} hello: {}",contact,hello) - wechaty.getPuppet().friendshipAdd(contact.id!!,hello).get() - } - fun isReady():Boolean{ return payload != null } @@ -67,17 +42,29 @@ class Friendship (wechaty: Wechaty):Accessory(wechaty){ if(payload!!.type != FriendshipType.Receive){ throw Exception("accept() need type to be FriendshipType.Receive, but it got a ${payload!!.type}") } - - wechaty.getPuppet().friendshipAccept(this.id!!).get() - + wechaty.getPuppet().friendshipAccept(this.id).get() val contact = contact() - contact.ready() - contact.sync() + } + fun hello():String{ + if(payload==null){ + throw Exception("ne payload") + } + return this.payload?.hello ?: ""; + } + + fun type():FriendshipType{ + return this.payload?.type ?:FriendshipType.Unknown } + fun toJson():String{ + if(payload==null){ + throw Exception("ne payload") + } + return JsonUtils.write(payload!!); + } companion object{ private val log = LoggerFactory.getLogger(Friendship::class.java) diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/Image.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/Image.kt index 302546d..0e55327 100644 --- a/wechaty/src/main/kotlin/io/github/wechaty/user/Image.kt +++ b/wechaty/src/main/kotlin/io/github/wechaty/user/Image.kt @@ -5,26 +5,18 @@ import io.github.wechaty.Wechaty import io.github.wechaty.filebox.FileBox import io.github.wechaty.schemas.ImageType -class Image(wechaty: Wechaty):Accessory(wechaty){ - - private var id:String?=null - - fun create(id:String):Image{ - val image = Image(wechaty) - image.id = id - return image - } +class Image(wechaty: Wechaty,val id:String):Accessory(wechaty){ fun thumbnail(): FileBox { - return wechaty.getPuppet().messageImage(id!!, ImageType.Thumbnail).get() + return wechaty.getPuppet().messageImage(id, ImageType.Thumbnail).get() } fun hd():FileBox{ - return wechaty.getPuppet().messageImage(id!!, ImageType.HD).get() + return wechaty.getPuppet().messageImage(id, ImageType.HD).get() } fun artwork():FileBox{ - return wechaty.getPuppet().messageImage(id!!, ImageType.Artwork).get() + return wechaty.getPuppet().messageImage(id, ImageType.Artwork).get() } -} \ No newline at end of file +} diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/Message.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/Message.kt index a8739e8..7dd15c9 100644 --- a/wechaty/src/main/kotlin/io/github/wechaty/user/Message.kt +++ b/wechaty/src/main/kotlin/io/github/wechaty/user/Message.kt @@ -283,6 +283,60 @@ open class Message(wechaty: Wechaty,val id: String) : Sayable, Accessory(wechaty } } + fun file():FileBox{ + return this.toFileBox() + } + + fun toImage():Image{ + if(this.type() != MessageType.Image){ + throw Exception("not a image type, type is "+ this.type()) + } + return wechaty.imageManager.create(this.id); + } + + fun toContact():Contact{ + + if(this.type() != MessageType.Contact){ + throw Exception("message not a ShareCard") + } + + val contactId = wechaty.getPuppet().messageContact(this.id).get() + if(StringUtils.isEmpty(contactId)){ + throw Exception("can not get contact id by message ${this.id}") + } + + val contact = wechaty.contactManager.load(contactId) + contact.ready() + return contact + } + + fun toUrlLink():UrlLink{ + if(this.type() != MessageType.Url){ + throw Exception("message not a Url Link") + } + + val urlPayload = wechaty.getPuppet().messageUrl(this.id).get() + return UrlLink(urlPayload) + } + + fun toMiniProgram():MiniProgram{ + + if(this.type() != MessageType.MiniProgram){ + throw Exception("message not a MiniProgram") + } + + val miniProgramPayload = wechaty.getPuppet().messageMiniProgram(this.id).get() + return MiniProgram(miniProgramPayload) + } + + + fun toFileBox():FileBox{ + if(this.type() == MessageType.Text){ + throw Exception("text message no file") + } + return wechaty.getPuppet().messageFile(this.id).get() + } + override fun toString():String{ return "Message(payload=$payload,id=$id)" diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/Room.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/Room.kt index 360fed5..9ed5058 100644 --- a/wechaty/src/main/kotlin/io/github/wechaty/user/Room.kt +++ b/wechaty/src/main/kotlin/io/github/wechaty/user/Room.kt @@ -75,12 +75,13 @@ class Room(wechaty: Wechaty, val id: String) : Accessory(wechaty), Sayable { is String -> { var mentionList = listOf() if (varList.isNotEmpty()) { - varList.forEach { + val list = varList[0] as? List<*> ?: throw Exception("room say contact args not valid") + list.forEach { if (it !is Contact) { throw Exception("mentionList must be contact when not using String array function call.") } } - mentionList = varList.toList() + mentionList = list as List val mentionAlias = mentionList.map { contact -> val alias = alias(contact as Contact) @@ -92,7 +93,7 @@ class Room(wechaty: Wechaty, val id: String) : Accessory(wechaty), Sayable { return@map "@$concatText" } val mentionText = mentionAlias.joinToString(separator = FOUR_PER_EM_SPACE) - text = mentionText + text = "$mentionText $something" } else { text = something } @@ -158,27 +159,27 @@ class Room(wechaty: Wechaty, val id: String) : Accessory(wechaty), Sayable { } } - fun onInvite(listener: InviteListener):Room{ - return on(EventEnum.INVITE,listener) + fun onInvite(listener: InviteListener): Room { + return on(EventEnum.INVITE, listener) } - fun onLeave(listener: LeaveListener):Room{ - return on(EventEnum.LEAVE,listener) + fun onLeave(listener: LeaveListener): Room { + return on(EventEnum.LEAVE, listener) } - fun onInnerMessage(listener: RoomInnerMessageListener):Room{ - return on(EventEnum.MESSAGE,listener) + fun onInnerMessage(listener: RoomInnerMessageListener): Room { + return on(EventEnum.MESSAGE, listener) } - fun onJoin(listener: JoinListener):Room{ - return on(EventEnum.JOIN,listener); + fun onJoin(listener: JoinListener): Room { + return on(EventEnum.JOIN, listener); } - fun onTopic(listener: TopicListener):Room{ - return on(EventEnum.TOPIC,listener) + fun onTopic(listener: TopicListener): Room { + return on(EventEnum.TOPIC, listener) } - private fun on(eventName: Event, listener: InviteListener):Room { + private fun on(eventName: Event, listener: InviteListener): Room { super.on(eventName, object : Listener { override fun handler(vararg any: Any) { listener.handler(any[0] as Contact, any[1] as RoomInvitation) @@ -187,7 +188,7 @@ class Room(wechaty: Wechaty, val id: String) : Accessory(wechaty), Sayable { return this } - private fun on(eventName: Event, listener: LeaveListener):Room { + private fun on(eventName: Event, listener: LeaveListener): Room { super.on(eventName, object : Listener { override fun handler(vararg any: Any) { listener.handler(any[0] as List, any[1] as Contact, any[2] as Date) @@ -196,7 +197,7 @@ class Room(wechaty: Wechaty, val id: String) : Accessory(wechaty), Sayable { return this } - private fun on(eventName: Event, listenerRoomInner: RoomInnerMessageListener):Room { + private fun on(eventName: Event, listenerRoomInner: RoomInnerMessageListener): Room { super.on(eventName, object : Listener { override fun handler(vararg any: Any) { listenerRoomInner.handler(any[0] as Message, any[1] as Date) @@ -205,7 +206,7 @@ class Room(wechaty: Wechaty, val id: String) : Accessory(wechaty), Sayable { return this } - private fun on(eventName: Event, listener: JoinListener):Room { + private fun on(eventName: Event, listener: JoinListener): Room { super.on(eventName, object : Listener { override fun handler(vararg any: Any) { listener.handler(any[0] as List, any[1] as Contact, any[2] as Date) @@ -214,7 +215,7 @@ class Room(wechaty: Wechaty, val id: String) : Accessory(wechaty), Sayable { return this } - private fun on(eventName: Event, listener: TopicListener):Room { + private fun on(eventName: Event, listener: TopicListener): Room { super.on(eventName, object : Listener { override fun handler(vararg any: Any) { listener.handler(any[0] as String, any[1] as String, any[2] as Contact, any[3] as Date) @@ -244,6 +245,57 @@ class Room(wechaty: Wechaty, val id: String) : Accessory(wechaty), Sayable { } } + fun getTopic(): Future { + + if (!isReady()) { + log.warn("Room topic() room not ready") + throw Exception("not ready") + } + + if (payload != null && payload!!.topic != null) { + return CompletableFuture.supplyAsync { + return@supplyAsync payload!!.topic + } + } else { + val memberIdList = puppet.roomMemberList(id).get() + val memberList = memberIdList.filter { it != puppet.selfId() } + .map { wechaty.contactManager.load(it) } + + var defaultTopic = "" + if (memberList.isNotEmpty()) { + defaultTopic = memberList[0].name() + } + + if (memberList.size >= 2) { + for (index in 1..2) { + defaultTopic += ",${memberList[index].name()}" + } + } + return CompletableFuture.supplyAsync { + return@supplyAsync defaultTopic + } + } + } + + fun setTopic(newTopic: String): Future { + if (!isReady()) { + log.warn("Room topic() room not ready") + throw Exception("not ready") + } + + return CompletableFuture.supplyAsync { + try { + val newTop = puppet.roomTopic(id, newTopic).get() + return@supplyAsync puppet.roomTopic(id, newTopic).get() + } catch (e: Exception) { + log.warn("Room topic(newTopic=$newTopic) exception:$e") + throw Exception(e) + } + } + + } + + @Deprecated("this function is deprecated! see getTopic,setTopic") fun topic(newTopic: String?): Future { if (!isReady()) { log.warn("Room topic() room not ready") @@ -355,9 +407,25 @@ class Room(wechaty: Wechaty, val id: String) : Accessory(wechaty), Sayable { return payload != null } + fun owner(): Contact? { + val ownerId = payload?.ownerId + + return if (ownerId.isNullOrBlank()) { + null + } else { + return wechaty.contactManager.load(ownerId) + } + } + + fun avatar(): FileBox { + log.debug("avatar:{}", avatar()) + return puppet.roomAvatar(this.id).get() + } + companion object { private val log = LoggerFactory.getLogger(Room::class.java) } + } val ROOM_EVENT_DICT = mapOf( diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/RoomInvitation.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/RoomInvitation.kt index 9859504..779ecb7 100644 --- a/wechaty/src/main/kotlin/io/github/wechaty/user/RoomInvitation.kt +++ b/wechaty/src/main/kotlin/io/github/wechaty/user/RoomInvitation.kt @@ -64,7 +64,7 @@ class RoomInvitation(wechaty: Wechaty,val id:String) : Accessory(wechaty){ fun topic():String { val payload = wechaty.getPuppet().roomInvitationPayload(this.id).get() - return payload.topic ?:""; + return payload.topic ?:"" } companion object{ diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/manager/FriendshipManager.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/FriendshipManager.kt new file mode 100644 index 0000000..889fa73 --- /dev/null +++ b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/FriendshipManager.kt @@ -0,0 +1,55 @@ +package io.github.wechaty.user.manager + +import io.github.wechaty.Accessory +import io.github.wechaty.Wechaty +import io.github.wechaty.schemas.FriendshipPayload +import io.github.wechaty.schemas.FriendshipSearchCondition +import io.github.wechaty.user.Contact +import io.github.wechaty.user.Friendship +import io.github.wechaty.utils.JsonUtils +import org.apache.commons.lang3.StringUtils +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +class FriendshipManager (wechaty: Wechaty): Accessory(wechaty){ + + fun load(id:String): Friendship { + return Friendship(wechaty,id) + } + + fun search(queryFilter: FriendshipSearchCondition): Contact?{ + log.debug("query filter {}",queryFilter) + val contactId = wechaty.getPuppet().friendshipSearch(queryFilter).get(); + if(StringUtils.isEmpty(contactId)){ + return null + } + val contact = wechaty.contactManager.load(contactId!!) + contact.ready() + return contact + } + + fun add(contact: Contact,hello:String){ + log.debug("add {},{}",contact,hello) + wechaty.getPuppet().friendshipAdd(contact.id,hello).get() + } + + fun del(contact: Contact){ + log.debug("del {}",contact) + throw Exception("to be implemented") + } + + fun fromJSON(payload:String):Friendship{ + val readValue = JsonUtils.readValue(payload) + return fromJSON(readValue) + } + + fun fromJSON(friendshipPayload: FriendshipPayload):Friendship{ + wechaty.getPuppet().friendshipPayload(friendshipPayload.id!!,friendshipPayload).get() + return load(friendshipPayload.id!!) + } + + companion object{ + private val log: Logger = LoggerFactory.getLogger(FriendshipManager::class.java) + } + +} diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/manager/ImageManager.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/ImageManager.kt new file mode 100644 index 0000000..da93025 --- /dev/null +++ b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/ImageManager.kt @@ -0,0 +1,18 @@ +package io.github.wechaty.user.manager + +import io.github.wechaty.Accessory +import io.github.wechaty.Wechaty +import io.github.wechaty.user.Image +import org.slf4j.LoggerFactory + +class ImageManager (wechaty: Wechaty): Accessory(wechaty){ + + fun create(id:String):Image{ + return Image(wechaty,id) + } + + companion object { + private val log = LoggerFactory.getLogger(ImageManager::class.java) + } + +} diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/manager/PuppetManager.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/PuppetManager.kt new file mode 100644 index 0000000..4920220 --- /dev/null +++ b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/PuppetManager.kt @@ -0,0 +1,49 @@ +package io.github.wechaty.user.manager + +import io.github.wechaty.Puppet +import io.github.wechaty.WechatyOptions +import io.github.wechaty.schemas.PuppetOptions +import io.github.wechaty.utils.JsonUtils +import org.reflections.Reflections +import org.reflections.util.ClasspathHelper +import org.reflections.util.ConfigurationBuilder +import org.slf4j.LoggerFactory +import java.util.concurrent.CompletableFuture +import java.util.concurrent.Future + +const val REFLECTION_BASE_PACKAGE = "io.github.wechaty" + +class PuppetManager { + + companion object { + private val log = LoggerFactory.getLogger(PuppetManager::class.java) + + @JvmStatic + fun resolveInstance(wechatyOptions: WechatyOptions): Future { + log.info("PuppetManager resolveInstance(${JsonUtils.write(wechatyOptions)})") + + val reflections = Reflections(ConfigurationBuilder().setUrls(ClasspathHelper.forPackage(REFLECTION_BASE_PACKAGE, Thread.currentThread().contextClassLoader))) + + val subTypes: Set<*> = reflections.getSubTypesOf(Puppet::class.java) + if (subTypes.isEmpty()) { + throw java.lang.RuntimeException("expect one puppet,but can not found any one.") + } + + val filterPuppet = subTypes.filter { + val clazz = it as Class<*> + clazz.name == wechatyOptions.puppet + } + + if (filterPuppet.size > 1) { + throw RuntimeException("expect one puppet,but found ${subTypes.size}") + } + val clazz = filterPuppet.first() as Class<*> + val declaredConstructor = clazz.getDeclaredConstructor(PuppetOptions::class.java) + return CompletableFuture.completedFuture(declaredConstructor.newInstance(wechatyOptions.puppetOptions!!) as Puppet) + } + } + + +} + + diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/manager/RoomManager.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/RoomManager.kt index 5c27a81..a08af52 100644 --- a/wechaty/src/main/kotlin/io/github/wechaty/user/manager/RoomManager.kt +++ b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/RoomManager.kt @@ -9,11 +9,10 @@ import io.github.wechaty.user.Contact import io.github.wechaty.user.Room import org.apache.commons.collections4.CollectionUtils import org.slf4j.LoggerFactory -import java.util.* class RoomManager(wechaty: Wechaty) : Accessory(wechaty) { - private val roomaCache: Cache = Caffeine.newBuilder().build() + private val roomCache: Cache = Caffeine.newBuilder().build() fun create(contactList: List, topic: String?): Room { if (contactList.size < 2) { @@ -83,7 +82,7 @@ class RoomManager(wechaty: Wechaty) : Accessory(wechaty) { } fun load(id: String): Room { - return roomaCache.get(id) { + return roomCache.get(id) { Room(wechaty, id) }!! } diff --git a/wechaty/src/test/kotlin/io/github/wechaty/user/RoomTest.kt b/wechaty/src/test/kotlin/io/github/wechaty/user/RoomTest.kt index 74dabce..50886d0 100644 --- a/wechaty/src/test/kotlin/io/github/wechaty/user/RoomTest.kt +++ b/wechaty/src/test/kotlin/io/github/wechaty/user/RoomTest.kt @@ -1,58 +1,69 @@ package io.github.wechaty.user +import io.github.wechaty.MockPuppet +import io.github.wechaty.Puppet import io.github.wechaty.Wechaty -import io.github.wechaty.utils.MockitoHelper +import io.github.wechaty.WechatyOptions +import io.github.wechaty.schemas.PuppetOptions import org.junit.After +import org.junit.Assert import org.junit.Before -import org.junit.Ignore import org.junit.Test -import org.mockito.ArgumentMatchers -import org.mockito.Mockito import org.mockito.Mockito.`when` -import org.mockito.Mockito.verify +import org.mockito.Mockito.spy /** * @author renxiaoya * @date 2020-05-29 */ +const val EXPECTED_ROOM_ID = "roomId" +const val EXPECTED_ROOM_TOPIC = "test-topic" +const val EXPECTED_CONTACT_1_ID = "contact1" +const val EXPECTED_CONTACT_1_ALIAS = "little1" +const val EXPECTED_CONTACT_2_ID = "contact2" +const val EXPECTED_CONTACT_2_ALIAS = "big2" + class RoomTest { - lateinit var mockWechaty: Wechaty + lateinit var wechaty: Wechaty lateinit var room: Room + lateinit var puppet: Puppet + @Before fun setUp() { - //TODO(create a mock puppet for unit test) - //these can not fill the inner field in wechaty.To resolve it,I may define a mock puppet for unit test - mockWechaty = Mockito.mock(Wechaty::class.java) - room = Mockito.mock(Room::class.java) + puppet = MockPuppet(PuppetOptions()) + val wechatyOptions = WechatyOptions() + wechatyOptions.name = "MockWechaty" + wechatyOptions.puppet = "io.github.wechaty.MockPuppet" + wechatyOptions.puppetOptions = PuppetOptions() + wechaty = Wechaty.instance(wechatyOptions) + wechaty.start() + room = wechaty.roomManager.load(EXPECTED_ROOM_ID) + room.sync() } @After fun tearDown() { - + wechaty.stop() } @Test - // ignore temporary because it doesn't work for now - @Ignore - fun say() { - `when`(room.alias(MockitoHelper.anyObject())).thenReturn("test-alias") - val msgId = "test_msgId" - `when`(mockWechaty.getPuppet().messageSendText(ArgumentMatchers.anyString() - , ArgumentMatchers.anyString() - , ArgumentMatchers.anyList()).get()).thenReturn(msgId) - val msg = Mockito.mock(Message::class.java) - `when`(mockWechaty.messageManager.load(msgId)).thenReturn(msg) + fun sayStringWithMentionList() { + val contact1 = wechaty.contactManager.load(EXPECTED_CONTACT_1_ID) + val contact2 = wechaty.contactManager.load(EXPECTED_CONTACT_2_ID) + contact1.sync() + contact2.sync() - val text = "test-text" - val contact1 = Contact(mockWechaty, "contact1") - val contact2 = Contact(mockWechaty, "contact2") - val contact3 = Contact(mockWechaty, "contact3") + val spyRoom: Room = spy(room) + `when`(spyRoom.alias(contact1)).thenReturn("test-contact1-alias") + `when`(spyRoom.alias(contact2)).thenReturn("test-contact2-alias") - verify(room.say(text, listOf(contact1, contact2, contact3))) + val text = "test-text" + val resMsg = spyRoom.say(text, listOf(contact1, contact2)).get() + Assert.assertEquals((resMsg as Message).id, "mock-msg-$EXPECTED_ROOM_ID") } }