diff --git a/examples/pom.xml b/examples/pom.xml
index f550c80..9fe8e48 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -33,6 +33,18 @@
wechaty
${wechaty.version}
+
+ io.github.wechaty
+ wechaty-puppet-hostie
+ ${wechaty.version}
+
+
+
+ io.github.wechaty
+ java-wechaty-plugin-contrib
+ 1.0.0-SNAPSHOT
+
+
org.apache.logging.log4j
log4j-api
@@ -62,11 +74,7 @@
1.7.30
-
- io.github.wechaty
- java-wechaty-plugin-contrib
- 1.0.0-SNAPSHOT
-
+
org.apache.commons
commons-lang3
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 4a0f34e..d096cd7 100644
--- a/examples/src/main/java/io/github/wechaty/example/Main.java
+++ b/examples/src/main/java/io/github/wechaty/example/Main.java
@@ -2,6 +2,7 @@
import io.github.wechaty.Wechaty;
+import io.github.wechaty.user.Contact;
import io.github.wechaty.user.Room;
import io.github.wechaty.utils.QrcodeUtils;
import okhttp3.OkHttpClient;
@@ -16,37 +17,24 @@ public class Main {
public static void main(String[] args){
Wechaty bot = Wechaty.instance("your_token")
- .onScan((qrcode, statusScanStatus, data) -> System.out.println(QrcodeUtils.getQr(qrcode)))
- .onLogin(user -> System.out.println(user))
+ .onScan((qrcode, statusScanStatus, data) -> {
+ System.out.println(QrcodeUtils.getQr(qrcode));
+ System.out.println("Online Image: https://wechaty.github.io/qrcode/" + qrcode);
+ })
+ .onLogin(user -> System.out.println(user.name() + "login"))
.onMessage(message -> {
Room room = message.room();
String text = message.text();
- if (StringUtils.equals(text, "#ding")) {
+ Contact from = message.from();
+ if (StringUtils.equals(text, "ding")) {
if (room != null) {
room.say("dong");
}
+ else {
+ // say something to from contact
+ from.say("hello:" + from.name());
+ }
}
}).start(true);
-
-// }
-
-// Room room = bot.room();
-//
-// RoomQueryFilter roomQueryFilter = new RoomQueryFilter();
-//
-// roomQueryFilter.setTopic("ChatOps - Donut");
-//
-// Future> all = room.findAll(roomQueryFilter);
-//
-// List rooms = all.get();
-//
-// Room room1 = rooms.get(0);
-//
-// FileBox fileBox = FileBox.fromFile("dong.jpg", "dong.jpg");
-//
-// room1.say(fileBox).get();
-
}
-
-
}
diff --git a/wechaty-puppet-hostie/pom.xml b/wechaty-puppet-hostie/pom.xml
index 9742318..59be0cd 100644
--- a/wechaty-puppet-hostie/pom.xml
+++ b/wechaty-puppet-hostie/pom.xml
@@ -62,7 +62,6 @@
org.apache.commons
commons-lang3
-
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 7e61a7a..c6132dd 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
@@ -389,7 +389,6 @@ class GrpcPuppet(puppetOptions: PuppetOptions) : Puppet(puppetOptions) {
}
override fun contactRawPayload(contractId: String): Future {
-
val request = Contact.ContactPayloadRequest.newBuilder()
.setId(contractId)
.build()
diff --git a/wechaty-puppet/aaaa.memory-card.json b/wechaty-puppet/aaaa.memory-card.json
new file mode 100644
index 0000000..d0d386b
--- /dev/null
+++ b/wechaty-puppet/aaaa.memory-card.json
@@ -0,0 +1 @@
+{"a":"b","\rparent\nmap":{"a":1,"b":2},"\rparent\nlist":["1","2","3"]}
\ No newline at end of file
diff --git a/wechaty-puppet/default.memory-card.json b/wechaty-puppet/default.memory-card.json
new file mode 100644
index 0000000..7f228f9
--- /dev/null
+++ b/wechaty-puppet/default.memory-card.json
@@ -0,0 +1 @@
+{"a":"b"}
\ No newline at end of file
diff --git a/wechaty-puppet/parent.memory-card.json b/wechaty-puppet/parent.memory-card.json
new file mode 100644
index 0000000..d0d386b
--- /dev/null
+++ b/wechaty-puppet/parent.memory-card.json
@@ -0,0 +1 @@
+{"a":"b","\rparent\nmap":{"a":1,"b":2},"\rparent\nlist":["1","2","3"]}
\ No newline at end of file
diff --git a/wechaty-puppet/pom.xml b/wechaty-puppet/pom.xml
index b68cd47..da7421e 100644
--- a/wechaty-puppet/pom.xml
+++ b/wechaty-puppet/pom.xml
@@ -21,7 +21,11 @@
org.jetbrains.kotlin
kotlin-stdlib
-
+
+ org.jetbrains.kotlinx
+ kotlinx-coroutines-core
+ 1.3.7
+
com.github.ben-manes.caffeine
@@ -40,15 +44,11 @@
commons-lang3
-
-
-
-
-
-
-
-
-
+
+ com.amazonaws
+ aws-java-sdk-s3
+ 1.11.636
+
@@ -105,7 +105,22 @@
okhttp
-
+
+
+ com.huaweicloud
+ esdk-obs-java
+ 3.19.7
+
+
+ com.aliyun.oss
+ aliyun-sdk-oss
+ 3.10.2
+
+
+ junit
+ junit
+ test
+
diff --git a/wechaty-puppet/src/main/kotlin/Puppet.kt b/wechaty-puppet/src/main/kotlin/Puppet.kt
index a91cdc1..fe12f5a 100644
--- a/wechaty-puppet/src/main/kotlin/Puppet.kt
+++ b/wechaty-puppet/src/main/kotlin/Puppet.kt
@@ -14,9 +14,11 @@ import io.github.wechaty.io.github.wechaty.watchdag.WatchDog
import io.github.wechaty.io.github.wechaty.watchdag.WatchdogFood
import io.github.wechaty.io.github.wechaty.watchdag.WatchdogListener
import io.github.wechaty.listener.*
+import io.github.wechaty.memorycard.MemoryCard
import io.github.wechaty.schemas.*
import io.github.wechaty.utils.FutureUtils
import io.github.wechaty.utils.JsonUtils
+import okhttp3.internal.toImmutableList
import org.apache.commons.collections4.CollectionUtils
import org.apache.commons.lang3.StringUtils
import org.slf4j.LoggerFactory
@@ -42,7 +44,7 @@ abstract class Puppet : EventEmitter {
private val HEARTBEAT_COUNTER = AtomicLong()
private val HOSTIE_KEEPALIVE_TIMEOUT = 15 * 1000L
private val DEFAULT_WATCHDOG_TIMEOUT = 60L
-// private var memory: MemoryCard
+ private var memory: MemoryCard
private val executorService = Executors.newSingleThreadScheduledExecutor()
@@ -61,15 +63,20 @@ abstract class Puppet : EventEmitter {
/**
*
*/
-
constructor(puppetOptions: PuppetOptions) {
count.addAndGet(1)
this.puppetOptions = puppetOptions
-// this.memory = MemoryCard()
-// this.memory.load()
-
+ // for test
+ this.memory = MemoryCard()
+ try {
+ this.memory.load()
+ log.debug("Puppet, constructor() memory.load() done")
+ }
+ catch (e: Exception) {
+ log.warn("Puppet, constructor() memory.load() rejection: {}", e)
+ }
val timeOut = puppetOptions.timeout ?: DEFAULT_WATCHDOG_TIMEOUT
watchDog = WatchDog(1000 * timeOut, "puppet")
@@ -441,13 +448,13 @@ abstract class Puppet : EventEmitter {
if (StringUtils.isNotBlank(query.id)) {
stream = stream?.filter {
- StringUtils.equals(query.alias, it.alias)
+ StringUtils.equals(query.id, it.id)
}
}
if (StringUtils.isNotBlank(query.weixin)) {
stream = stream?.filter {
- StringUtils.equals(query.alias, it.alias)
+ StringUtils.equals(query.weixin, it.weixin)
}
}
@@ -700,8 +707,46 @@ abstract class Puppet : EventEmitter {
}
- protected fun messageQueryFilterFactory(query: MessageQueryFilter) {
- TODO()
+ protected fun messageQueryFilterFactory(query: MessageQueryFilter): MessagePayloadFilterFunction {
+ val clz = query::class.java
+ val fields = clz.fields
+ val list = fields.map {
+ it.name to it.get(query)
+ }
+
+ var filterFunctionList = ArrayList()
+
+ list.forEach { pair ->
+ if (StringUtils.isNotEmpty(pair.second.toString())) {
+ val filterFunction = if (StringUtils.equals(pair.first.toString(), "textReg")) {
+ {
+ payload: MessagePayload -> Boolean
+ val clazz = payload::class.java
+ val field = clazz.getField(pair.first)
+ val realValue = field.get(payload).toString()
+ Regex(pair.second.toString()).matches(realValue)
+ }
+ }
+ else {
+ {
+ payload: MessagePayload -> Boolean
+ val clazz = payload::class.java
+ val field = clazz.getField(pair.first)
+ val realValue = field.get(payload).toString()
+ StringUtils.equals(realValue, pair.second.toString())
+ }
+ }
+ filterFunctionList.add(filterFunction)
+ }
+ }
+ val allFilterFunction: MessagePayloadFilterFunction = {
+ payload: MessagePayload ->
+ filterFunctionList.all {
+ func -> func(payload)
+ }
+ }
+ return allFilterFunction
+
}
fun messageForward(conversationId: String, messageId: String): Future {
@@ -819,37 +864,36 @@ abstract class Puppet : EventEmitter {
abstract fun roomMemberList(roomId: String): Future>
fun roomMemberSearch(roomId: String, query: RoomMemberQueryFilter): Future> {
- TODO()
- }
-
- fun roomReach(query: RoomQueryFilter?): Future> {
- TODO()
- }
+ return CompletableFuture.supplyAsync {
+ val roomMemberIdList = roomMemberList(roomId).get()
- fun roomValidate(roomId: String): Future {
- return CompletableFuture.completedFuture(true)
- }
+ val contactQuery = if (query.name != null || query.contactAlias != null) {
+ val contactQueryFilter = ContactQueryFilter()
+ contactQueryFilter.name = query.name
+ contactQueryFilter
+ }
+ else {
+ val contactQueryFilter = ContactQueryFilter()
+ contactQueryFilter.alias = query.contactAlias
+ contactQueryFilter
+ }
- fun roomPayloadCache(roomId: String): RoomPayload? {
- return cacheRoomPayload.getIfPresent(roomId)
- }
+ var idList = ArrayList(contactSearch(contactQuery, roomMemberIdList).get())
+ val memberPayloadList = roomMemberIdList.mapNotNull { x -> roomMemberPayload(roomId, x).get() }
- fun roomPayload(roomId: String): Future {
- return CompletableFuture.supplyAsync {
- return@supplyAsync cacheRoomPayload.get(roomId) { t: String ->
- val get = roomRawPayload(t).get()
- return@get roomRawPayloadParser(get).get()
+ if (query.roomAlias != null) {
+ val result = memberPayloadList.filter { payload ->
+ payload.roomAlias === query.roomAlias
+ }.map { payload ->
+ payload.id
+ }
+ idList.addAll(result)
}
+ return@supplyAsync idList;
}
}
- fun roomPayloadDirty(roomId: String): Future {
- cacheRoomPayload.invalidate(roomId)
- return CompletableFuture.completedFuture(null)
- }
-
-
fun roomSearch(query: RoomQueryFilter): Future> {
return CompletableFuture.supplyAsync {
val allRoomList = roomList().get()
@@ -879,6 +923,30 @@ abstract class Puppet : EventEmitter {
}
}
+ fun roomValidate(roomId: String): Future {
+ return CompletableFuture.completedFuture(true)
+ }
+
+ fun roomPayloadCache(roomId: String): RoomPayload? {
+ return cacheRoomPayload.getIfPresent(roomId)
+ }
+
+
+ fun roomPayload(roomId: String): Future {
+ return CompletableFuture.supplyAsync {
+ return@supplyAsync cacheRoomPayload.get(roomId) { t: String ->
+ val get = roomRawPayload(t).get()
+ return@get roomRawPayloadParser(get).get()
+ }
+ }
+ }
+
+ fun roomPayloadDirty(roomId: String): Future {
+ cacheRoomPayload.invalidate(roomId)
+ return CompletableFuture.completedFuture(null)
+ }
+
+
/**
* Concat roomId & contactId to one string
*/
@@ -912,13 +980,15 @@ abstract class Puppet : EventEmitter {
}
}
-// fun setMemory(memoryCard: MemoryCard){
-// this.memory = memoryCard
-// }
+ fun setMemory(memoryCard: MemoryCard) {
+ log.debug("Puppet, setMemory()")
-// fun getEventBus(): EventBus {
-// return eb
-// }
+ this.memory = memoryCard
+ }
+
+ override fun toString(): String {
+ return "Puppet#${count}<${this.puppetOptions?.name}>(${this.memory.getName()})"
+ }
companion object {
private val log = LoggerFactory.getLogger(Puppet::class.java)
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/StateEnum.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/StateEnum.kt
index fb54b6f..798027a 100644
--- a/wechaty-puppet/src/main/kotlin/io/github/wechaty/StateEnum.kt
+++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/StateEnum.kt
@@ -1,7 +1,7 @@
package io.github.wechaty
-enum class StateEnum {
+import io.github.wechaty.eventEmitter.Event
+enum class StateEnum: Event {
PENDING,ON,OFF;
-
-}
\ No newline at end of file
+}
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/eventEmitter/EventEmitter.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/eventEmitter/EventEmitter.kt
index 9e60edf..27b532c 100644
--- a/wechaty-puppet/src/main/kotlin/io/github/wechaty/eventEmitter/EventEmitter.kt
+++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/eventEmitter/EventEmitter.kt
@@ -84,7 +84,6 @@ open class EventEmitter : EventEmitterInterface {
}
map.put(event, wrapListener)
-
}
override fun removeAllListeners(event:Event): Boolean {
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/MemoryCard.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/MemoryCard.kt
index e6864fd..f6753a3 100644
--- a/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/MemoryCard.kt
+++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/MemoryCard.kt
@@ -1,89 +1,407 @@
-//package io.github.wechaty.memorycard
-//
-//import org.slf4j.LoggerFactory
-//import java.util.concurrent.Future
-//
-//class MemoryCard{
-//
-// private var name:String? = null
-// protected var parent:MemoryCard? = null
-// protected var payload:MemoryCardPayload ? = null
-// protected var storage:StorageBackend? = null
-// protected val multiplexNameList = mutableListOf()
-//
-// private var options:MemoryCardOptions
-//
-// constructor(name: String?=null,options:MemoryCardOptions? = null){
-//
-// val _optiones:MemoryCardOptions = options ?: MemoryCardOptions()
-//
-// if(name != null){
-// if(options != null) {
-// _optiones.name = name
-// }
-// }
-// this.options = _optiones
-// this.name = _optiones.name
-//
-// (_optiones.multiplex != null).let {
-// this.parent = _optiones.multiplex!!.parent
-// this.payload = this.parent!!.payload
-// this.multiplexNameList.addAll(parent!!.multiplexNameList)
-// this.multiplexNameList.add(_optiones.multiplex!!.name)
-// this.storage = null
-// }
-//
-// (_optiones.multiplex == null).let {
-// this.payload = null
-// this.multiplexNameList.clear()
-// }
-// }
-//
-// private fun getStore():StorageBackend?{
-// log.debug("getStorage() for storage type: %s'",this.options)
-//
-// return StorageBackend.getStorage(
-// this.options.name!!,
-// this.options.storageOptions
-// )
-// }
-//
-// fun load(){
-// this.payload = this.storage!!.load()
-// }
-//
-// fun save(){
-// this.storage!!.save(this.payload!!)
-// }
-//
-// companion object{
-// private val log = LoggerFactory.getLogger(MemoryCard::class.java)
-//
-// fun multiplex(memory:MemoryCard,name:String):MemoryCard{
-// return MemoryCard()
-// }
-// }
-//
-//
-//}
-//
-//class MemoryCardPayload{
-// val map:Map = HashMap()
-//}
-//
-//data class Multiplex(
-// val parent:MemoryCard,
-// val name:String
-//)
-//
-//class MemoryCardOptions{
-//
-// var name:String? = null
-// var storageOptions:StorageBackendOptions? = null
-// var multiplex:Multiplex? = null
-//
-//}
-//
-//
-//
-//
+package io.github.wechaty.memorycard
+
+import io.github.wechaty.utils.JsonUtils
+import org.slf4j.LoggerFactory
+
+const val NAMESPACE_MULTIPLEX_SEPRATOR = "\r"
+const val NAMESPACE_KEY_SEPRATOR = "\n"
+
+val NAMESPACE_MULTIPLEX_SEPRATOR_REGEX = Regex(NAMESPACE_MULTIPLEX_SEPRATOR)
+val NAMESPACE_KEY_SEPRATOR_REGEX = Regex(NAMESPACE_KEY_SEPRATOR)
+
+// 名字可以由options传入也可以直接传入
+class MemoryCard {
+
+ private var name:String? = null
+ protected var parent:MemoryCard? = null
+ protected var payload:MemoryCardPayload ? = null
+ protected var storage:StorageBackend? = null
+ protected val multiplexNameList = mutableListOf()
+
+ // 是否是multiplex,用哪个存储后端,memorycard名字是什么
+ private var options:MemoryCardOptions
+
+ // name和options里面的name有可能同时为空
+ constructor(name: String? = null, options: MemoryCardOptions? = null) {
+ val _options: MemoryCardOptions = options ?: MemoryCardOptions()
+ log.info("MemoryCard, constructor({})", JsonUtils.write(_options))
+
+ if(name != null) {
+ _options.name = name
+ }
+
+ else if (_options.name != null) {
+ this.name = _options.name
+ }
+ else {
+ // 如果没有给名字就用一个default作为名字
+ this.name = "default"
+ _options.name = "default"
+ }
+ this.options = _options
+
+ if (_options.multiplex != null) {
+ this.parent = _options.multiplex!!.parent
+ this.payload = this.parent!!.payload
+
+ this.multiplexNameList.addAll(parent!!.multiplexNameList)
+ this.multiplexNameList.add(_options.multiplex!!.name)
+
+ this.storage = null
+ }
+ else {
+ this.payload = null
+ this.multiplexNameList.clear()
+ this.storage = getStore()
+ }
+ }
+
+ private fun getStore(): StorageBackend? {
+
+ log.debug("getStorage() for storage type: {}",
+ if (this.options != null && this.options.storageOptions != null && this.options.storageOptions!!.type != null)
+ this.options
+ else
+ "N/A"
+ )
+
+ if (this.options == null) {
+ return null
+ }
+
+ // 默认得到一个file的后端
+ return StorageBackend.getStorage(
+ this.options.name!!,
+ this.options.storageOptions
+ )
+ }
+
+ suspend fun loadAsync() {
+ log.info("MemoryCard, load() from storage: {}", this.storage ?: "N/A")
+ if (this.isMultiplex()) {
+ log.info("MemoryCard, load() should not be called on a multiplex MemoryCard. NOOP")
+ return
+ }
+ if (this.payload != null) {
+ throw Exception("memory had already loaded before.")
+ }
+
+ if (this.storage != null) {
+ this.payload = this.storage!!.load()
+ }
+ else {
+ log.info("MemoryCard, load() no storagebackend")
+ this.payload = MemoryCardPayload()
+ }
+ }
+
+ fun load() {
+ log.info("MemoryCard, load() from storage: {}", this.storage ?: "N/A")
+ if (this.isMultiplex()) {
+ log.info("MemoryCard, load() should not be called on a multiplex MemoryCard. NOOP")
+ return
+ }
+ if (this.payload != null) {
+ throw Exception("memory had already loaded before.")
+ }
+
+ if (this.storage != null) {
+ this.payload = this.storage!!.load()
+ }
+ else {
+ log.info("MemoryCard, load() no storagebackend")
+ this.payload = MemoryCardPayload()
+ }
+ }
+
+ fun save() {
+ if (this.isMultiplex()) {
+ if (this.parent == null) {
+ throw Exception("multiplex memory no parent")
+ }
+ this.parent!!.save()
+ }
+
+ log.info("MemoryCard, <{}>{} save() to {}",this.name ?: "", this.multiplexPath(), this.storage ?: "N/A")
+ if (this.payload == null) {
+ throw Exception("no payload, please call load() first.")
+ }
+
+ if (this.storage == null) {
+ log.info("MemoryCard, save() no storage, NOOP")
+ return
+ }
+
+ this.storage!!.save(this.payload!!)
+ }
+
+ fun destory() {
+ log.info("MemoryCard, destroy() storage: {}", this.storage ?: "N/A")
+ if (this.isMultiplex()) {
+ throw Exception("can not destroy on a multiplexed memory")
+ }
+
+ // this.clear()
+
+ if (this.storage != null) {
+ this.storage!!.destory()
+ this.storage = null
+ }
+ this.payload = null
+ }
+
+
+ fun size(): Int {
+ log.info("MemoryCard, <{}> size", this.multiplexPath())
+
+ if (this.payload == null) {
+ throw Exception("no payload, please call load() first.")
+ }
+
+ val count: Int
+ if (this.isMultiplex()) {
+ count = this.payload!!.map.keys
+ .filter { key -> this.isMultiplexKey(key) }
+ .size
+ }
+ else {
+ count = this.payload!!.map.size
+ }
+
+ return count
+ }
+
+
+ fun isMultiplex (): Boolean {
+ return this.multiplexNameList.size > 0
+ }
+
+ protected fun multiplexPath(): String {
+ return this.multiplexNameList.joinToString("/")
+ }
+
+ protected fun multiplexNamespace(): String {
+ if (!this.isMultiplex()) {
+ throw Exception("not a multiplex memory")
+ }
+
+ val namespace = NAMESPACE_MULTIPLEX_SEPRATOR +
+ this.multiplexNameList.joinToString(NAMESPACE_MULTIPLEX_SEPRATOR)
+ return namespace
+ }
+
+ protected fun isMultiplexKey(key: String): Boolean {
+
+ if (NAMESPACE_MULTIPLEX_SEPRATOR_REGEX.containsMatchIn(key)
+ && NAMESPACE_KEY_SEPRATOR_REGEX.containsMatchIn(key)) {
+
+ val namespace = this.multiplexNamespace()
+ return key.startsWith(namespace)
+ }
+ return false
+ }
+
+ protected fun resolveKey (name: String): String {
+
+ if (this.isMultiplex()) {
+ val namespace = this.multiplexNamespace()
+ return namespace + NAMESPACE_KEY_SEPRATOR + name
+ }
+ else {
+ return name
+ }
+ }
+
+ fun get(name: String): Any? {
+ log.info("MemoryCard, <{}> get({})", this.multiplexPath(), name)
+ if (this.payload == null) {
+ throw Exception("no payload, please call load() first.")
+ }
+
+ val key = this.resolveKey(name)
+ return this.payload!!.map.get(key)
+ }
+ /**
+ * 功能描述:
+ *
+ * @Param:
+ * @Return:
+ * @Author: a1725
+ * @Date: 2020/7/18 23:54
+ */
+ fun set(name: String, data: T) {
+ log.info("MemoryCard, <{}> set({}, {})", this.multiplexPath(), name, data)
+
+ if (this.payload == null) {
+ throw Exception("no payload, please call load() first.")
+ }
+
+ val key = this.resolveKey(name)
+ this.payload!!.map[key] = data as Any
+ }
+
+ fun clear() {
+ log.info("MemoryCard, <{}> clear()", this.multiplexPath())
+
+ if (this.payload == null) {
+ throw Exception("no payload, please call load() first.")
+ }
+ if (this.isMultiplex()) {
+ val keys = this.payload!!.map.keys
+ for (key in keys) {
+ this.payload!!.map.remove(key)
+ }
+ }
+ else {
+ this.payload = MemoryCardPayload()
+ }
+ }
+
+ fun delete(name: String) {
+ log.info("MemoryCard, <{}> delete({})", this.multiplexPath(), name)
+ if (this.payload == null) {
+ throw Exception("no payload, please call load() first.")
+ }
+
+ val key = this.resolveKey(name)
+ this.payload!!.map.remove(key)
+ }
+
+ fun entries(): MutableSet> {
+ log.info("MemoryCard, <{}> *entries()", this.multiplexPath())
+
+ if (this.payload == null) {
+ throw Exception("no payload, please call load() first.")
+ }
+
+ return this.payload!!.map.entries
+ }
+
+ fun has(key: String): Boolean {
+ log.info("MemoryCard, <{}> has ({})", this.multiplexPath(), key)
+
+ if (this.payload == null) {
+ throw Exception("no payload, please call load() first.")
+ }
+
+ val absoluteKey = this.resolveKey(key)
+ return this.payload!!.map.containsKey(absoluteKey)
+ }
+
+ fun keys(): MutableSet {
+ log.info("MemoryCard, <{}> keys()", this.multiplexPath())
+
+ if (this.payload == null) {
+ throw Exception("no payload, please call load() first.")
+ }
+ var result = mutableSetOf()
+ for (key in this.payload!!.map.keys) {
+ if (this.isMultiplex()) {
+ if (this.isMultiplexKey(key)) {
+ val namespace = this.multiplexNamespace()
+ val mpKey = key.substring(namespace.length + 1)
+ result.add(mpKey)
+ }
+ continue
+ }
+ result.add(key)
+ }
+ return result
+ }
+
+
+ fun values(): MutableCollection {
+ log.info("MemoryCard, <{}> values()", this.multiplexPath())
+
+ if (this.payload == null) {
+ throw Exception("no payload, please call load() first.")
+ }
+
+ return this.payload!!.map.values
+ }
+
+
+
+ override fun toString(): String {
+ var mpString = ""
+ if (this.multiplexNameList.size > 0) {
+ mpString = this.multiplexNameList
+ .map { mpName -> "multiplex(${mpName})" }
+ .joinToString("")
+ }
+ var name = ""
+ if (this.options != null && this.options.name != null) {
+ name = this.options.name.toString()
+ }
+
+ return "MemoryCard<${name}>${mpString}"
+ }
+
+ fun getVersion(): String {
+ return VERSION
+ }
+
+ fun getName(): String? {
+ return this.name
+ }
+ // 会将当前的类作为parent, 后面那个为namespace
+ fun multiplex(nameSpace: String): MemoryCard {
+ log.info("MemoryCard, multiplex({})", nameSpace)
+ return multiplex(this, nameSpace)
+ }
+
+ protected fun multiplex(parent: MemoryCard, nameSpace: String): MemoryCard {
+ log.info("MemoryCard, multiplex({}, {})", parent, nameSpace)
+ parent.options.name = parent.name
+ parent.options.multiplex = Multiplex(name = nameSpace, parent = parent)
+ val mpMemory = MemoryCard(options = parent.options)
+ return mpMemory
+ }
+
+ companion object {
+ private val log = LoggerFactory.getLogger(MemoryCard::class.java)
+ val VERSION = "0.0.0"
+
+ fun fromJSON(text: String): MemoryCard {
+ log.info("MemoryCard, fromJSON(...)")
+ var jsonObj: MemoryCardJsonObject
+ jsonObj = JsonUtils.readValue(text)
+ val card = MemoryCard(jsonObj.options.name, jsonObj.options)
+ card.payload = jsonObj.payload
+ return MemoryCard()
+ }
+
+ fun fromJSON(obj: MemoryCardJsonObject): MemoryCard {
+ log.info("MemoryCard, fromJSON(...)")
+ val card = MemoryCard(obj.options.name, obj.options)
+ card.payload = obj.payload
+
+ return card
+ }
+ }
+}
+
+class MemoryCardPayload{
+ var map = mutableMapOf()
+}
+
+data class Multiplex(
+ val parent: MemoryCard,
+ val name: String
+)
+
+data class MemoryCardOptions(
+ var name: String? = null,
+ var storageOptions:StorageBackendOptions? = null,
+ var multiplex:Multiplex? = null
+)
+
+data class MemoryCardJsonObject(
+ val payload: MemoryCardPayload,
+ val options: MemoryCardOptions
+)
+
+
+
+
+
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/StorageFile.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/StorageFile.kt
deleted file mode 100644
index f4647a8..0000000
--- a/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/StorageFile.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-//package io.github.wechaty.memorycard
-//
-//import io.github.wechaty.utils.JsonUtils
-//import org.apache.commons.io.FileUtils
-//import org.apache.commons.io.FilenameUtils
-//import org.apache.commons.lang3.StringUtils
-//import java.io.File
-//
-//class StorageFile(val name: String, var options: StorageBackendOptions) : StorageBackend(name,options) {
-//
-// private var absFileName:String
-//
-// init {
-// options.type = "file"
-// options = options as StorageFileOptions
-// val file = File(name)
-// if(file.isAbsolute){
-// this.absFileName = name
-// }else{
-// this.absFileName = FilenameUtils.concat(System.getProperty("user.dir"),name)
-// }
-//
-// if(!StringUtils.endsWith(this.absFileName,".memory-card.json")){
-// this.absFileName += ".memory-card.json"
-// }
-//
-// }
-//
-// override fun save(payload: MemoryCardPayload) {
-// val text = JsonUtils.write(payload)
-// val file = File(absFileName)
-// FileUtils.write(file,text,"UTF-8")
-//
-// }
-//
-// override fun load(): MemoryCardPayload {
-// val file = File(absFileName)
-// if(!file.exists()){
-// return MemoryCardPayload()
-// }
-// val text = FileUtils.readFileToString(file, "UTF-8")
-// return JsonUtils.readValue(text);
-//
-// }
-//
-// override fun destory() {
-// TODO("Not yet implemented")
-// }
-//
-//}
-//
-//
-//
-//fun main(){
-//
-// StorageFile("test",StorageFileOptions())
-//
-//}
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/StorageOptions.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/StorageOptions.kt
index f2d32a8..8cb4043 100644
--- a/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/StorageOptions.kt
+++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/StorageOptions.kt
@@ -1,30 +1,42 @@
-//package io.github.wechaty.memorycard
-//
-//sealed class StorageBackendOptions{
-// var type:String?=null
-//}
-//
-//data class StorageS3Options(
-// val accessKeyId:String,
-// val secretAccessKey:String,
-// val region:String,
-// val bucket:String
-//):StorageBackendOptions()
-//
-//
-//data class StorageObsOptions(
-// val accessKeyId:String,
-// val secretAccessKey:String,
-// val server:String,
-// val bucket:String
-//):StorageBackendOptions()
-//
-//class StorageNopOptions: StorageBackendOptions() {
-// var placeholder: String? = null
-//}
-//
-//typealias StorageFileOptions = StorageNopOptions
-//
-//val BACKEND_DICT = mapOf(
-// "file" to StorageFile::class
-//)
+package io.github.wechaty.memorycard
+
+import io.github.wechaty.memorycard.backend.*
+
+
+sealed class StorageBackendOptions {
+ var type: String? = null
+}
+
+class StorageNopOptions: StorageBackendOptions()
+
+typealias StorageFileOptions = StorageNopOptions
+
+data class StorageS3Options(
+ val accessKeyId:String,
+ val secretAccessKey:String,
+ val region:String,
+ val bucket:String
+): StorageBackendOptions()
+
+
+data class StorageObsOptions(
+ val accessKeyId:String,
+ val secretAccessKey:String,
+ val server:String,
+ val bucket:String
+): StorageBackendOptions()
+
+data class StorageOSSOptions(
+ val accessKeyId: String,
+ val secretAccessKey: String,
+ val endPoint: String,
+ val bucket: String
+): StorageBackendOptions()
+
+val BACKEND_DICT = mapOf(
+ "file" to StorageFile::class,
+ "s3" to StorageS3::class,
+ "nop" to StorageNop::class,
+ "obs" to StorageObs::class,
+ "oss" to StorageOSS::class
+)
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/StrorageBackend.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/StrorageBackend.kt
index 6879ab5..ed1020c 100644
--- a/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/StrorageBackend.kt
+++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/StrorageBackend.kt
@@ -1,39 +1,39 @@
-//package io.github.wechaty.memorycard
-//
-//import org.slf4j.LoggerFactory
-//import kotlin.reflect.full.createInstance
-//import kotlin.reflect.full.primaryConstructor
-//
-//abstract class StorageBackend(name: String, option: StorageBackendOptions) {
-//
-// init {
-// log.debug("constructor({}, { type: {} })",name,option)
-// }
-//
-// abstract fun save(payload: MemoryCardPayload)
-// abstract fun load():MemoryCardPayload
-// abstract fun destory()
-//
-// companion object{
-// private val log = LoggerFactory.getLogger(StorageBackend::class.java)
-//
-// fun getStorage(name: String,options: StorageBackendOptions?):StorageBackend{
-//
-// var _options = options
-//
-// if(options == null) {
-// _options = StorageFileOptions()
-// _options.type = "file"
-// }
-//
-// if(_options?.type == null || _options.type !in BACKEND_DICT.keys){
-// throw Exception("backed unknown : ${_options?.type}")
-// }
-//
-//
-// }
-//
-// }
-//
-//}
-//
+package io.github.wechaty.memorycard
+
+import io.github.wechaty.memorycard.backend.StorageFile
+import io.github.wechaty.utils.JsonUtils
+import org.slf4j.LoggerFactory
+
+abstract class StorageBackend(name: String, option: StorageBackendOptions) {
+
+ init {
+ log.debug("constructor({}, { type: {} })", name, option)
+ }
+
+ abstract fun save(payload: MemoryCardPayload)
+ abstract fun load():MemoryCardPayload
+ abstract fun destory()
+
+ companion object{
+ private val log = LoggerFactory.getLogger(StorageBackend::class.java)
+
+ fun getStorage(name: String, options: StorageBackendOptions?): StorageBackend {
+ log.info("getStorage', name: {}, options: {}", name, options?.let { JsonUtils.write(it) })
+
+ var _options = options
+
+ // 如果没有传option参数,默认用file后端
+ if(options == null) {
+ _options = StorageFileOptions()
+ _options.type = "file"
+ }
+
+ if(_options?.type == null || _options.type!! !in BACKEND_DICT.keys) {
+ throw Exception("backed unknown : ${_options?.type}")
+ }
+ return StorageFile(name, _options)
+ }
+ }
+
+}
+
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageFile.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageFile.kt
new file mode 100644
index 0000000..003ffb3
--- /dev/null
+++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageFile.kt
@@ -0,0 +1,108 @@
+package io.github.wechaty.memorycard.backend
+
+import io.github.wechaty.memorycard.MemoryCardPayload
+import io.github.wechaty.memorycard.StorageBackend
+import io.github.wechaty.memorycard.StorageBackendOptions
+import io.github.wechaty.memorycard.StorageFileOptions
+import io.github.wechaty.utils.JsonUtils
+import org.apache.commons.io.FileUtils
+import org.apache.commons.io.FilenameUtils
+import org.apache.commons.lang3.StringUtils
+import org.slf4j.LoggerFactory
+import java.io.File
+import java.io.IOException
+import java.lang.Exception
+
+class StorageFile(val name: String, var options: StorageBackendOptions) : StorageBackend(name, options) {
+
+ private var absFileName: String
+
+ init {
+
+ log.info("StorageFile, constructor(%s, ...)", name)
+
+ options.type = "file"
+ options = options as StorageFileOptions
+
+ val file = File(name)
+
+ if(file.isAbsolute) {
+ this.absFileName = name
+ }
+ else {
+ this.absFileName = FilenameUtils.concat(System.getProperty("user.dir"),name)
+ }
+
+ if(!StringUtils.endsWith(this.absFileName,".memory-card.json")) {
+ this.absFileName += ".memory-card.json"
+ }
+
+ }
+
+ override fun load(): MemoryCardPayload {
+ log.info("StorageFile, load() from %s", this.absFileName)
+
+ val file = File(absFileName)
+ if(!file.exists()){
+ return MemoryCardPayload()
+ }
+
+ var text = ""
+ try {
+ text = FileUtils.readFileToString(file, "UTF-8")
+ }
+ catch (e: IOException) {
+ log.error("load() from file %s error %s", this.absFileName, e.toString())
+ }
+
+ val payload = MemoryCardPayload()
+ try {
+ payload.map = JsonUtils.readValue(text)
+ }
+ catch (e: Exception) {
+ log.error("MemoryCard, load() exception: %s", e)
+ }
+ return payload
+ }
+
+ override fun save(payload: MemoryCardPayload) {
+ log.info("StorageFile, save() to %s", this.absFileName)
+
+ val text = JsonUtils.write(payload.map)
+ val file = File(absFileName)
+ try {
+ FileUtils.write(file,text,"UTF-8")
+ }
+ catch (e: IOException) {
+ log.error("MemoryCard, save() exception: %s", e)
+ }
+ }
+
+ override fun destory() {
+ log.info("StorageFile, destoay()")
+
+ val file = File(absFileName)
+ if (file.exists()) {
+ val deleteQuietly = FileUtils.deleteQuietly(file)
+ if (deleteQuietly) {
+ log.info("destory() ${this.absFileName} success")
+ }
+ else {
+ log.warn("destory() ${this.absFileName} failed")
+ }
+ }
+ }
+
+ override fun toString(): String {
+ return "${this.name}<${this.absFileName}>"
+ }
+ companion object {
+ private val log = LoggerFactory.getLogger(StorageFile::class.java)
+ }
+}
+
+
+
+fun main() {
+ val storageFile = StorageFile("test", StorageFileOptions())
+}
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageNop.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageNop.kt
new file mode 100644
index 0000000..db1b20c
--- /dev/null
+++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageNop.kt
@@ -0,0 +1,41 @@
+package io.github.wechaty.memorycard.backend
+
+import io.github.wechaty.memorycard.*
+import org.slf4j.LoggerFactory
+
+class StorageNop(val name: String, var options: StorageBackendOptions) : StorageBackend(name,options) {
+
+ init {
+ log.info("StorageNop, constructor(%s, ...)", name)
+ options.type = "nop"
+ options = options as StorageNopOptions
+ }
+
+ override fun load(): MemoryCardPayload {
+ log.info("StorageNop, load()")
+ return MemoryCardPayload()
+ }
+
+ override fun save(payload: MemoryCardPayload) {
+ log.info("StorageNop, save()")
+ }
+
+ override fun destory() {
+ log.info("StorageNop, destroy()")
+ }
+
+ override fun toString(): String {
+ return "${this.name} "
+ }
+
+ companion object {
+ private val log = LoggerFactory.getLogger(StorageNop::class.java)
+ }
+}
+
+
+
+fun main() {
+ val storageNop = StorageNop("test", StorageNopOptions())
+ println(storageNop)
+}
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageOSS.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageOSS.kt
new file mode 100644
index 0000000..fa2cc7c
--- /dev/null
+++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageOSS.kt
@@ -0,0 +1,112 @@
+package io.github.wechaty.memorycard.backend
+
+import com.aliyun.oss.OSSClient
+import com.aliyun.oss.OSSClientBuilder
+import com.aliyun.oss.model.PutObjectRequest
+import io.github.wechaty.memorycard.*
+import io.github.wechaty.utils.JsonUtils
+import org.slf4j.LoggerFactory
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+
+// 使用阿里的云存储服务
+class StorageOSS(val name: String, var options: StorageBackendOptions) : StorageBackend(name,options) {
+
+ private var oss: OSSClient
+
+ init {
+ log.info("StorageOSS, constructor()")
+ options.type = "oss"
+ options = options as StorageOSSOptions
+ val _options = options as StorageOSSOptions
+ this.oss = OSSClientBuilder().build(_options.endPoint,_options.accessKeyId, _options.secretAccessKey) as OSSClient
+ }
+
+ override fun save(payload: MemoryCardPayload) {
+ log.info("StorageOSS, save()")
+ this.putObject(payload)
+ }
+
+ override fun load(): MemoryCardPayload {
+ log.info("StorageOSS, load()")
+ val card = this.getObject()
+ log.info("press", card)
+ return card
+ }
+
+ override fun destory() {
+ log.info("StorageOSS, destroy()")
+ this.deleteObject()
+ }
+
+ private fun putObject(payload: MemoryCardPayload) {
+ val options = this.options as StorageOSSOptions
+
+ val putObjectRequest = PutObjectRequest(options.bucket, this.name, ByteArrayInputStream(JsonUtils.write(payload.map).toByteArray()))
+ try {
+ this.oss.putObject(putObjectRequest)
+ }
+ catch (e: Exception) {
+ log.error("上传${this.name}错误")
+ }
+ }
+
+ private fun getObject(): MemoryCardPayload {
+ val options = this.options as StorageOSSOptions
+ val ossObject = try {
+ this.oss.getObject(options.bucket, this.name)
+ }
+ catch (e: Exception) {
+ log.error("获取文件${this.name}失败")
+ null
+ }
+ if (ossObject == null) {
+ return MemoryCardPayload()
+ }
+ val input = ossObject.objectContent
+ val byte = ByteArray(1024)
+ val bos = ByteArrayOutputStream()
+ var len = 0;
+ while (true) {
+ len = input.read(byte)
+ if (len != -1) {
+ bos.write(byte, 0, len)
+ }
+ else {
+ break
+ }
+ }
+ input.close()
+ ossObject.close()
+ val card = MemoryCardPayload()
+ card.map = JsonUtils.readValue(String(bos.toByteArray()))
+ return card
+ }
+
+ private fun deleteObject() {
+ val options = this.options as StorageOSSOptions
+ try {
+ this.oss.deleteObject(options.bucket, this.name)
+ }
+ catch (e: Exception) {
+ log.error("删除${this.name}错误")
+ }
+ }
+
+ fun shutdown() {
+ log.info("StorageOSS, shutdown()")
+ this.oss.shutdown()
+ }
+
+ override fun toString(): String {
+ return "${this.name}<${this.name}>"
+ }
+
+ companion object {
+ private val log = LoggerFactory.getLogger(StorageObs::class.java)
+ }
+}
+
+fun main() {
+
+}
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageObs.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageObs.kt
new file mode 100644
index 0000000..71153b7
--- /dev/null
+++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageObs.kt
@@ -0,0 +1,131 @@
+package io.github.wechaty.memorycard.backend
+
+import com.amazonaws.auth.AWSStaticCredentialsProvider
+import com.amazonaws.auth.BasicAWSCredentials
+import com.amazonaws.regions.Regions
+import com.amazonaws.services.s3.AmazonS3
+import com.amazonaws.services.s3.AmazonS3ClientBuilder
+import com.obs.services.ObsClient
+import io.github.wechaty.memorycard.*
+import io.github.wechaty.utils.JsonUtils
+import org.apache.commons.io.FileUtils
+import org.apache.commons.io.FilenameUtils
+import org.apache.commons.lang3.StringUtils
+import org.slf4j.LoggerFactory
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.File
+
+// 使用华为的云存储服务
+class StorageObs(val name: String, var options: StorageBackendOptions) : StorageBackend(name,options) {
+
+ private var obs: ObsClient
+
+ init {
+ log.info("StorageObs, constructor()")
+ options.type = "obs"
+ options = options as StorageObsOptions
+ var _options = options as StorageObsOptions
+ this.obs = ObsClient(_options.accessKeyId, _options.secretAccessKey, _options.server)
+ }
+
+ override fun save(payload: MemoryCardPayload) {
+ log.info("StorageObs, save()")
+ this.putObject(payload)
+ }
+
+ override fun load(): MemoryCardPayload {
+ log.info("StorageObs, load()")
+ val card = this.getObject()
+ log.info("press", card)
+ return card
+ }
+
+ override fun destory() {
+ log.info("StorageObs, destroy()")
+ this.deleteObject()
+ }
+
+ private fun putObject(payload: MemoryCardPayload) {
+ val options = this.options as StorageObsOptions
+
+ val putObject = this.obs.putObject(options.bucket, this.name, ByteArrayInputStream(JsonUtils.write(payload.map).toByteArray()))
+ if (putObject.statusCode >= 300) {
+ throw Exception("obs putObject error")
+ }
+ }
+
+ private fun getObject(): MemoryCardPayload {
+ val options = this.options as StorageObsOptions
+
+ val obsObject = try {
+ this.obs.getObject(options.bucket, this.name)
+ }
+ catch (e: Exception) {
+ log.error("获取${name}错误")
+ null
+ }
+
+ if (obsObject == null) {
+ return MemoryCardPayload()
+ }
+ val input = obsObject.objectContent
+ var byte = ByteArray(1024)
+ val bos = ByteArrayOutputStream()
+ var len = 0;
+ while (true) {
+ len = input.read(byte)
+ if (len != -1) {
+ bos.write(byte, 0, len)
+ }
+ else {
+ break
+ }
+ }
+
+ input.close()
+ var card = MemoryCardPayload()
+ card.map = JsonUtils.readValue(String(bos.toByteArray()))
+ return card
+ }
+
+ private fun deleteObject() {
+ val options = this.options as StorageObsOptions
+ val deleteObject = try {
+ this.obs.deleteObject(options.bucket, this.name)
+ }
+ catch (e: Exception) {
+ log.error("删除${name}错误")
+ null
+ }
+ if (deleteObject == null) {
+ throw Exception("obs deleteObject error")
+ }
+ if (deleteObject.statusCode >= 300) {
+ throw Exception("obs deleteObject error")
+ }
+ }
+ override fun toString(): String {
+ return "${this.name}<${this.name}>"
+ }
+
+ fun shutdown() {
+ log.info("StorageObs, shutdown()")
+ this.obs.close()
+ }
+
+ companion object {
+ private val log = LoggerFactory.getLogger(StorageObs::class.java)
+ }
+}
+
+
+
+fun main(){
+ val storageObsOptions = StorageObsOptions("aaa", "aaa",
+ "aaa", "aaa")
+
+ val storageObs = StorageObs("notexist", storageObsOptions)
+ val load = storageObs.load()
+
+}
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageS3.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageS3.kt
new file mode 100644
index 0000000..aa7522e
--- /dev/null
+++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/memorycard/backend/StorageS3.kt
@@ -0,0 +1,103 @@
+package io.github.wechaty.memorycard.backend
+
+import com.amazonaws.auth.AWSStaticCredentialsProvider
+import com.amazonaws.auth.BasicAWSCredentials
+import com.amazonaws.services.s3.AmazonS3
+import com.amazonaws.services.s3.AmazonS3ClientBuilder
+import io.github.wechaty.memorycard.*
+import io.github.wechaty.utils.JsonUtils
+import org.slf4j.LoggerFactory
+
+class StorageS3(val name: String, var options: StorageBackendOptions) : StorageBackend(name,options) {
+
+ private var s3: AmazonS3
+
+ init {
+ log.info("StorageS3, constructor()")
+ options.type = "s3"
+ options = options as StorageS3Options
+ val _options = options as StorageS3Options
+
+ val basicAWSCredentials = BasicAWSCredentials(_options.accessKeyId, _options.secretAccessKey)
+ this.s3 = AmazonS3ClientBuilder.standard().withCredentials(AWSStaticCredentialsProvider(basicAWSCredentials))
+ .withRegion(_options.region).build()
+ }
+
+ override fun save(payload: MemoryCardPayload) {
+ log.info("StorageS3, save()")
+ val options = this.options as StorageS3Options
+ try {
+ this.s3.putObject(JsonUtils.write(payload.map), options.bucket, this.name)
+ }
+ catch (e: Exception) {
+ log.error("上传文件:${this.name}错误")
+ }
+ }
+
+ override fun load(): MemoryCardPayload {
+ log.info("StorageS3, load()")
+
+ val options = this.options as StorageS3Options
+ val result = try {
+ this.s3.getObject(options.bucket, this.name)
+ }
+ catch (e: Exception) {
+ log.error("获取文件:${this.name}错误")
+ null
+ }
+
+ if (result == null || result.objectContent == null) {
+ return MemoryCardPayload()
+ }
+ val objectContent = result.objectContent
+
+ var payloadMap = StringBuffer()
+ var readBuf = ByteArray(1024)
+ var readLen = 0
+ while (true) {
+ readLen = objectContent.read(readBuf)
+ if (readLen > 0) {
+ payloadMap.append(String(readBuf, 0, readLen))
+ }
+ else {
+ break
+ }
+ }
+
+ var payload = MemoryCardPayload()
+ payload.map = JsonUtils.readValue(payloadMap.toString())
+ return payload
+ }
+
+ override fun destory() {
+ log.info("StorageS3, destory()")
+ val options = this.options as StorageS3Options
+ try {
+ this.s3.deleteObject(options.bucket, this.name)
+ }
+ catch (e: Exception) {
+ log.error("删除${this.name}错误")
+ }
+ }
+
+ override fun toString(): String {
+ return "${this.name}<${this.name}>"
+ }
+
+ fun shutdown() {
+ log.info("StorageS3, shutdown()")
+ this.s3.shutdown()
+ }
+
+ companion object {
+ private val log = LoggerFactory.getLogger(StorageS3::class.java)
+ }
+
+}
+
+fun main(){
+
+// val storageS3 = StorageS3("test", StorageS3Options("1", "1", "2", "3"))
+// val load = storageS3.load()
+
+}
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/Message.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/Message.kt
index 7b5e02d..d1cd7cc 100644
--- a/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/Message.kt
+++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/schemas/Message.kt
@@ -61,6 +61,7 @@ class MessageQueryFilter {
override fun toString(): String {
return "MessageQueryFilter(fromId=$fromId, id=$id, roomId=$roomId, text=$text, toId=$toId, type=$type, textReg=$textReg)"
}
-
-
}
+
+typealias MessagePayloadFilterFunction = (payload: MessagePayload) -> Boolean
+typealias MessagePayloadFilterFactory = (query: MessageQueryFilter) -> MessagePayloadFilterFunction
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/status/StateListener.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/status/StateListener.kt
new file mode 100644
index 0000000..2c57339
--- /dev/null
+++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/status/StateListener.kt
@@ -0,0 +1,8 @@
+package io.github.wechaty.io.github.wechaty.status
+
+import io.github.wechaty.StateEnum
+
+@FunctionalInterface
+interface StateSwitchListener {
+ fun handler(state: StateEnum)
+}
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/status/StateSwitch.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/status/StateSwitch.kt
index f3aa3f8..acf78de 100644
--- a/wechaty-puppet/src/main/kotlin/io/github/wechaty/status/StateSwitch.kt
+++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/status/StateSwitch.kt
@@ -1,17 +1,19 @@
-package io.github.wechaty.io.github.wechaty.status
+package io.github.wechaty.status
import io.github.wechaty.StateEnum
import io.github.wechaty.eventEmitter.EventEmitter
+import io.github.wechaty.eventEmitter.Listener
import io.github.wechaty.io.github.wechaty.schemas.EventEnum
+import io.github.wechaty.io.github.wechaty.status.StateSwitchListener
+import kotlinx.coroutines.*
import org.slf4j.LoggerFactory
-import java.util.concurrent.CompletableFuture
-import java.util.concurrent.Future
+import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.atomic.AtomicInteger
+import javax.swing.plaf.nimbus.State
-val COUNTER=AtomicInteger()
-
-class StateSwitch:EventEmitter(){
-
+var COUNTER = AtomicInteger()
+val resolver: () -> Unit = {}
+class StateSwitch(var name :String = "#${COUNTER.addAndGet(1)}"): EventEmitter(){
@Volatile
private var onoff:Boolean = false
@@ -19,42 +21,187 @@ class StateSwitch:EventEmitter(){
@Volatile
private var pending:Boolean = false
+ private lateinit var onPromise : suspend () -> Unit
+ private lateinit var offPromise : suspend () -> Unit
- private lateinit var onPromise : Future
- private lateinit var offPromise : Future
+ private lateinit var onResolver : () -> Unit
+ private lateinit var offResolver : () -> Unit
- private lateinit var onResolver : Function
- private lateinit var offResolver : Function
+// private var name :String = "#${COUNTER.addAndGet(1)}"
+ private val VERSION: String = "0.0.0"
+ private lateinit var onQueue: ArrayBlockingQueue<() -> Unit>
+ private lateinit var offQueue: ArrayBlockingQueue<() -> Unit>
+ init {
+ log.info("StateSwitch, constructor({})", this.name)
- private val name :String = "#${COUNTER.addAndGet(1)}"
+ this.onoff = false
+ this.pending = false
+ onQueue = ArrayBlockingQueue(1)
+ offQueue = ArrayBlockingQueue(1)
+ this.offQueue.put(resolver)
- init {
- offPromise = CompletableFuture.completedFuture(null);
- onPromise = CompletableFuture.runAsync{
- TODO()
- }
}
- fun on(state: StateEnum):StateEnum{
- val on = on()
- log.debug("statusSwitch $name on ${state.name} <- $on")
+ // 传入on的状态只能是on或者说pending
+ fun on(state: StateEnum): StateEnum {
+ log.debug("statusSwitch $name on ${state.name} <- ${this.on()}")
+
+ if (state == StateEnum.OFF) {
+ throw Exception("the parameter state shouldn't be OFF")
+ }
onoff = true
pending = (state == StateEnum.PENDING)
- emit(EventEnum.ON,state.name)
- TODO()
+ emit(EventEnum.ON, state.name)
+
+ if (this.offQueue.isEmpty()) {
+ this.offQueue.put(resolver)
+ }
+
+ if (state == StateEnum.ON && this.onQueue.isEmpty()) {
+ this.onQueue.put(resolver)
+ }
+ return this.on()
+
+ }
+ // get the current on state
+ fun on(): StateEnum {
+ val on =
+ if (this.onoff == true)
+ if (this.pending == true)
+ StateEnum.PENDING
+ else
+ StateEnum.ON
+ else
+ StateEnum.OFF
+ log.info("StateSwitch, <{}> on() is {}", this.name, on)
+ return on
+ }
+
+ fun off(state: StateEnum): StateEnum {
+ log.info("StateSwitch, <{}> off({}) <- ({})", this.name, state, this.off())
+ if (state == StateEnum.ON) {
+ throw Exception("the parameter state shouldn't be ON")
+ }
+
+ this.onoff = false
+ this.pending = (state == StateEnum.PENDING)
+ this.emit(StateEnum.OFF, state)
+
+ if (this.onQueue.isEmpty()) {
+ this.onQueue.put(resolver)
+ }
+ if (state == StateEnum.OFF && this.offQueue.isEmpty()) {
+ this.offQueue.put(resolver)
+ }
+ return this.off()
}
- fun on():StateEnum{
- TODO()
+ /**
+ *
+ * @return 返回当前off的状态
+ */
+ fun off(): StateEnum {
+ val off =
+ if (!this.onoff)
+ if (this.pending)
+ StateEnum.PENDING
+ else
+ StateEnum.OFF
+ else
+ StateEnum.ON
+ log.info("StateSwitch, <{}> off() is {}", this.name, off)
+ return off
}
- companion object{
- private val log = LoggerFactory.getLogger(StateSwitch::class.java)
+ /**
+ * @param state: 准备变为的状态
+ * @param cross: 是否变换状态,默认可以
+ * 好像可以去掉runblocking
+ */
+ fun ready(state: StateEnum = StateEnum.ON, cross: Boolean = true) = runBlocking {
+ log.info("StateSwitch, <{}> ready({}, {})", name, state, cross)
+
+ // 如果准备变换的状态为on
+ if (state == StateEnum.ON) {
+ // 如果当前状态为off,并且不允许变换状态
+ if (onoff == false && cross == false) {
+ throw Exception("ready(on) but the state is off. call ready(on, true) to force crossWait")
+ }
+ // 当前状态为on
+ // 或者说当前状态为off, 但是允许变换状态
+ // his.onPromise
+ CoroutineScope(Dispatchers.Default).launch {
+ onQueue.take()
+ }
+ }
+ // 如果准备变为off
+ else if (state == StateEnum.OFF) {
+ // 但是当前状态为 on, 并且不允许变换状态
+ if (onoff == true && cross == false) {
+ throw Exception("ready(off) but the state is on. call ready(off, true) to force crossWait")
+ }
+ // 当前状态为off,或者说当前状态为on, 但是允许变换状态
+ // 执行状态改变时执行的函数
+ CoroutineScope(Dispatchers.Default).launch {
+ offQueue.take()
+ }
+ }
+ // 错误状态
+ else {
+ throw Exception("should not go here. ${state} should be of type 'never'")
+ }
+ log.info("StateSwitch, <{}> ready({}, {}) resolved.", name, state, cross)
+ }
+
+ fun addEventListener(type: StateEnum, listener: StateSwitchListener) {
+ super.addListener(type, object : Listener {
+ override fun handler(vararg any: Any) {
+ listener.handler(any[0] as StateEnum)
+ }
+
+ })
}
+ fun removeEventListener(type: StateEnum, listener: StateSwitchListener) {
+ super.removeListener(type, object : Listener {
+ override fun handler(vararg any: Any) {
+ // 有问题
+ listener.handler(any[0] as StateEnum)
+ }
+ })
+ }
+ fun version(): String {
+ return this.VERSION
+ }
+ companion object {
+ private val log = LoggerFactory.getLogger(StateSwitch::class.java)
+ }
}
+
+fun main() {
+
+ val stateSwitch = StateSwitch()
+ println("刚开始创建时:" + stateSwitch.on())
+
+ stateSwitch.on(StateEnum.PENDING)
+ println("调用on(pending):" + stateSwitch.on())
+ stateSwitch.ready(StateEnum.ON)
+ println("调用ready(on)之后:" + stateSwitch.on())
+ stateSwitch.on(StateEnum.ON)
+ println("调用on(on):" + stateSwitch.on())
+ // ======================================
+// stateSwitch.off(StateEnum.PENDING)
+// println(stateSwitch.off())
+//
+// stateSwitch.ready(StateEnum.OFF)
+// println(stateSwitch.on())
+//
+// stateSwitch.off(StateEnum.OFF)
+// println(stateSwitch.on())
+
+}
diff --git a/wechaty-puppet/src/main/kotlin/io/github/wechaty/utils/JsonUtils.kt b/wechaty-puppet/src/main/kotlin/io/github/wechaty/utils/JsonUtils.kt
index 70bd984..a775dc0 100644
--- a/wechaty-puppet/src/main/kotlin/io/github/wechaty/utils/JsonUtils.kt
+++ b/wechaty-puppet/src/main/kotlin/io/github/wechaty/utils/JsonUtils.kt
@@ -15,11 +15,10 @@ object JsonUtils {
return mapper.readValue(json)
}
- fun write(input:Any):String{
+ fun write(input:Any):String {
return mapper.writeValueAsString(input)
}
-
}
diff --git a/wechaty-puppet/src/test/java/MemoryCardTest.java b/wechaty-puppet/src/test/java/MemoryCardTest.java
new file mode 100644
index 0000000..7959595
--- /dev/null
+++ b/wechaty-puppet/src/test/java/MemoryCardTest.java
@@ -0,0 +1,20 @@
+import io.github.wechaty.memorycard.MemoryCard;
+import org.junit.Test;
+
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+
+public class MemoryCardTest {
+
+ @Test
+ public void testLoad() throws ExecutionException, InterruptedException {
+ MemoryCard card = new MemoryCard("default", null);
+ card.load();
+ Map map = (Map) card.get("person");
+ if (map != null) {
+ map.forEach((key, value) -> {
+ System.out.println(key + ":" + value);
+ });
+ }
+ }
+}
diff --git a/wechaty-puppet/test.memory-card.json b/wechaty-puppet/test.memory-card.json
new file mode 100644
index 0000000..9d908d9
--- /dev/null
+++ b/wechaty-puppet/test.memory-card.json
@@ -0,0 +1 @@
+{"\rparent\nson":"b","list":[1,2,3]}
\ No newline at end of file
diff --git a/wechaty/pom.xml b/wechaty/pom.xml
index 10aa138..3f46585 100644
--- a/wechaty/pom.xml
+++ b/wechaty/pom.xml
@@ -16,13 +16,17 @@
-
io.github.wechaty
wechaty-puppet
${project.version}
-
+
+
+ io.github.wechaty
+ wechaty-puppet-hostie
+ ${project.version}
+
io.github.wechaty
wechaty-puppet-hostie
@@ -92,8 +96,6 @@
okhttp
-
-
org.hamcrest
hamcrest-core
@@ -113,6 +115,7 @@
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 10c9997..ba9daa0 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/Wechaty.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/Wechaty.kt
@@ -4,17 +4,21 @@ 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.filebox.FileBox
import io.github.wechaty.io.github.wechaty.schemas.EventEnum
import io.github.wechaty.listener.*
-//import io.github.wechaty.memorycard.MemoryCard
+import io.github.wechaty.memorycard.MemoryCard
import io.github.wechaty.schemas.*
+import io.github.wechaty.status.StateSwitch
import io.github.wechaty.user.*
import io.github.wechaty.user.manager.*
import org.slf4j.LoggerFactory
import java.util.*
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.Future
import java.util.concurrent.locks.ReentrantLock
-
+val PUPPET_MEMORY_NAME = "puppet"
class Wechaty private constructor(private var wechatyOptions: WechatyOptions) : EventEmitter() {
private val LOCK = ReentrantLock()
@@ -26,12 +30,12 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) :
private val globalPluginList: MutableList = mutableListOf()
@Volatile
- private var readyState = StateEnum.OFF
+ private var readyState = StateSwitch("Wechaty")
@Volatile
- private var status = StateEnum.OFF
+ private var status = StateSwitch("WechatyReady")
-// private var memory:MemoryCard? = null
+ private var memory: MemoryCard? = null
val tagManager: TagManager = TagManager(this)
val contactManager = ContactManager(this)
@@ -39,18 +43,31 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) :
val roomManager = RoomManager(this)
val roomInvitationManager = RoomInvitationManager(this)
val imageManager = ImageManager(this)
- val friendshipManager = FriendshipManager(this)
+ val friendShipManager = FriendshipManager(this)
init {
-// this.memory = wechatyOptions.memory
+ if (wechatyOptions.memory == null) {
+ this.memory = MemoryCard(wechatyOptions.name)
+ }
+ else {
+ this.memory = wechatyOptions.memory
+ }
installGlobalPlugin()
}
fun start(await: Boolean = false):Wechaty {
+ if (this.status.on() == StateEnum.ON) {
+ this.status.ready(StateEnum.ON)
+ return this
+ }
+
+ this.readyState.off(StateEnum.OFF)
+ this.status.on(StateEnum.PENDING)
initPuppet()
puppet.start().get()
- status = StateEnum.ON
+// status = StateEnum.ON
+ status.on(StateEnum.ON)
emit(EventEnum.START, "")
if (await) {
@@ -69,7 +86,16 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) :
}
fun stop() {
+// if (this.status.off() == StateEnum.OFF) {
+// this.status.ready(StateEnum.OFF)
+// return
+// }
+//
+// this.readyState.off(StateEnum.OFF)
+// this.status.off(StateEnum.PENDING)
puppet.stop()
+
+// this.status.off(StateEnum.OFF)
}
fun logout(){
@@ -99,12 +125,22 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) :
return on(EventEnum.LOGIN,listener)
}
+ fun onLogout(listener: LogoutListener): Wechaty {
+ return on(EventEnum.LOGOUT, listener)
+ }
fun onScan(listener: ScanListener):Wechaty{
return on(EventEnum.SCAN,listener);
}
+ fun onReady(listener: ReadyListener): Wechaty {
+ return on(EventEnum.READY, listener)
+ }
+ fun onFriendship(listener: FriendshipListener): Wechaty {
+ return on(EventEnum.FRIENDSHIP, listener)
+ }
+
fun onRoomJoin(listener: RoomJoinListener):Wechaty {
- return on(EventEnum.ROOM_JOIN,listener)
+ return on(EventEnum.ROOM_JOIN, listener)
}
fun onRoomLeave(listener: RoomLeaveListener):Wechaty {
@@ -115,10 +151,16 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) :
return on(EventEnum.ROOM_TOPIC,listener)
}
+ fun onRoomInvite(listener: RoomInviteListener): Wechaty {
+ return on(EventEnum.ROOM_INVITE, listener)
+ }
fun onMessage(listener: MessageListener):Wechaty{
return on(EventEnum.MESSAGE,listener)
}
+ fun onError(listener: ErrorListener): Wechaty {
+ return on(EventEnum.ERROR, listener)
+ }
fun use(vararg plugins: WechatyPlugin):Wechaty{
plugins.forEach {
it(this)
@@ -140,11 +182,36 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) :
})
return this
}
+ private fun on(event: Event,listener: ReadyListener):Wechaty{
+ super.on(event, object : Listener {
+ override fun handler(vararg any: Any) {
+ listener.handler()
+ }
+ })
+ return this
+ }
+
+ private fun on(event: Event, listener: LogoutListener): Wechaty {
+ super.on(event, object : Listener {
+ override fun handler(vararg any: Any) {
+ listener.handler(any[0] as String, any[1] as String)
+ }
+ })
+ return this
+ }
private fun on(event: Event, listener: DongListener):Wechaty {
return this
}
+ private fun on(event: Event, listener: ErrorListener): Wechaty {
+ super.on(event, object : Listener {
+ override fun handler(vararg any: Any) {
+ listener.handler(any[0] as String)
+ }
+ })
+ return this
+ }
private fun on(event: Event, listener: ScanListener):Wechaty{
super.on(event, object : Listener {
override fun handler(vararg any: Any) {
@@ -162,7 +229,24 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) :
})
return this
}
+ private fun on(event: Event, listener: FriendshipListener): Wechaty {
+ super.on(event, object : Listener {
+ override fun handler(vararg any: Any) {
+ listener.handler(any[0] as Friendship)
+ }
+ })
+ return this
+ }
+ private fun on(eventName: Event, listener: RoomInviteListener): Wechaty {
+ super.on(eventName, object : Listener {
+ override fun handler(vararg any: Any) {
+ // roomInvitationId
+ listener.handler(any[0] as RoomInvitation)
+ }
+ })
+ return this
+ }
private fun on(eventName: Event, listener: RoomJoinListener):Wechaty {
super.on(eventName, object : Listener {
override fun handler(vararg any: Any) {
@@ -192,6 +276,11 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) :
private fun initPuppet() {
this.puppet = PuppetManager.resolveInstance(wechatyOptions).get()
+ if (this.memory == null) {
+ throw Exception("no memory")
+ }
+ val puppetMemory = this.memory!!.multiplex(PUPPET_MEMORY_NAME)
+ this.puppet.setMemory(puppetMemory)
initPuppetEventBridge(puppet)
}
@@ -247,7 +336,7 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) :
EventEnum.FRIENDSHIP -> {
puppet.on(it, object : PuppetFriendshipListener {
override fun handler(payload: EventFriendshipPayload) {
- val friendship = friendshipManager.load(payload.friendshipId)
+ val friendship = friendShipManager.load(payload.friendshipId)
friendship.ready()
emit(EventEnum.FRIENDSHIP, friendship)
}
@@ -268,7 +357,7 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) :
override fun handler(payload: EventLogoutPayload) {
val contact = contactManager.loadSelf(payload.contactId)
contact.ready()
- emit(EventEnum.LOGOUT, contact, payload.data)
+ emit(EventEnum.LOGOUT, contact.id, payload.data)
}
})
}
@@ -290,7 +379,8 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) :
puppet.on(it, object : PuppetReadyListener {
override fun handler(payload: EventReadyPayload) {
emit(EventEnum.READY);
- readyState = StateEnum.ON
+// readyState = StateEnum.ON
+ readyState.on(StateEnum.ON)
}
})
}
@@ -331,7 +421,7 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) :
puppet.on(it, object : PuppetRoomLeaveListener {
override fun handler(payload: EventRoomLeavePayload) {
val room = roomManager.load(payload.roomId)
- room.sync()
+ room.sync().get()
val leaverList = payload.removeeIdList.map { id ->
val contact = contactManager.loadSelf(id)
@@ -353,12 +443,11 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) :
puppet.on(it, object : PuppetRoomTopicListener {
override fun handler(payload: EventRoomTopicPayload) {
val room = roomManager.load(payload.roomId)
- room.sync()
+ room.sync().get()
val changer = contactManager.loadSelf(payload.changerId)
changer.ready()
val date = Date(payload.timestamp * 1000)
-
emit(EventEnum.ROOM_TOPIC, room, payload.newTopic, payload.oldTopic, changer, date)
room.emit(EventEnum.TOPIC, payload.newTopic, payload.oldTopic, changer, date)
}
@@ -417,6 +506,26 @@ class Wechaty private constructor(private var wechatyOptions: WechatyOptions) :
}
}, "StartMain-shutdown-hook"))
}
+
+// override fun toString(): String {
+// if (this.wechatyOptions == null) {
+// return "default"
+// }
+// val first = if (this.wechatyOptions != null && this.puppet != null) {
+// this.wechatyOptions.puppet
+// }
+// else {
+// "puppet"
+// }
+//
+// val second = if (this.memory != null) {
+// this.memory!!.getName()
+// }
+// else {
+// "default"
+// }
+// return "Wechaty#<${first}><${second}>"
+// }
}
diff --git a/wechaty/src/main/kotlin/io/github/wechaty/WechatyListener.kt b/wechaty/src/main/kotlin/io/github/wechaty/WechatyListener.kt
index 8da5e0b..a76da29 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/WechatyListener.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/WechatyListener.kt
@@ -1,10 +1,7 @@
package io.github.wechaty
import io.github.wechaty.schemas.ScanStatus
-import io.github.wechaty.user.Contact
-import io.github.wechaty.user.ContactSelf
-import io.github.wechaty.user.Message
-import io.github.wechaty.user.Room
+import io.github.wechaty.user.*
import java.util.*
@FunctionalInterface
@@ -19,7 +16,7 @@ interface ErrorListener {
@FunctionalInterface
interface FriendshipListener {
- fun handler(friendshipId: String)
+ fun handler(friendship: Friendship)
}
@FunctionalInterface
@@ -66,7 +63,7 @@ interface RoomTopicListener {
@FunctionalInterface
interface RoomInviteListener {
- fun handler(roomInvitationId: String)
+ fun handler(roomInvitation : RoomInvitation)
}
@FunctionalInterface
@@ -77,6 +74,5 @@ interface ReadyListener {
@FunctionalInterface
interface MessageListener {
fun handler(message: Message)
-
}
diff --git a/wechaty/src/main/kotlin/io/github/wechaty/WechatyOptions.kt b/wechaty/src/main/kotlin/io/github/wechaty/WechatyOptions.kt
index d930bf7..5d337f1 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/WechatyOptions.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/WechatyOptions.kt
@@ -1,11 +1,11 @@
package io.github.wechaty
-//import io.github.wechaty.memorycard.MemoryCard
+import io.github.wechaty.memorycard.MemoryCard
import io.github.wechaty.schemas.PuppetOptions
class WechatyOptions {
-// var memory:MemoryCard? = null
+ var memory: MemoryCard? = null
var name:String = "Wechaty"
@@ -17,5 +17,5 @@ class WechatyOptions {
var ioToken:String? = null
}
- typealias WechatyPlugin = (Wechaty) -> Unit
+typealias WechatyPlugin = (Wechaty) -> Unit
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 8edd437..56c6b36 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/user/Contact.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/Contact.kt
@@ -94,10 +94,41 @@ open class Contact(wechaty: Wechaty,val id:String) : Sayable, Accessory(wechaty)
return (payload != null && StringUtils.isNotEmpty(payload!!.name))
}
- fun name():String{
+ fun name():String {
return payload?.name ?: ""
}
+ fun getName():String? {
+ return payload?.name
+ }
+
+ fun address(): String {
+ return this.payload?.address ?: ""
+ }
+
+ fun getAddress(): String? {
+ return this.payload?.address
+ }
+
+ fun signature(): String {
+ return this.payload?.signature ?: ""
+ }
+ fun getSignature(): String? {
+ return this.payload?.signature
+ }
+
+ fun star(): Boolean {
+ return this.payload?.star ?: false
+ }
+
+ fun weixin(): String {
+ return this.payload?.weixin ?: ""
+ }
+
+ fun getWeixin(): String? {
+ return this.payload?.weixin
+ }
+
fun setAlias(newAlias:String){
if(payload == null){
throw Exception("no payload")
@@ -113,8 +144,8 @@ open class Contact(wechaty: Wechaty,val id:String) : Sayable, Accessory(wechaty)
}
- fun getAlias():String?{
- return payload?.alias ?:null
+ fun getAlias():String? {
+ return payload?.alias
}
fun stranger():Boolean?{
@@ -132,28 +163,54 @@ open class Contact(wechaty: Wechaty,val id:String) : Sayable, Accessory(wechaty)
fun type():ContactType{
return payload?.type ?: throw Exception("no payload")
}
+ fun getType(): ContactType {
+ return payload?.type ?: throw Exception("no payload")
+ }
fun gender():ContactGender{
return payload?.gender ?: ContactGender.Unknown
}
+ fun getGender():ContactGender{
+ return payload?.gender ?: throw Exception("no payload")
+ }
fun province():String?{
return payload?.province
}
+ fun getProvince():String?{
+ return payload?.province
+ }
fun city():String?{
return payload?.city
}
+ fun getCity():String?{
+ return payload?.city
+ }
+
open fun avatar(): FileBox {
try {
return wechaty.getPuppet().getContactAvatar(this.id).get()
} catch (e: Exception) {
log.error("error",e)
- TODO()
+ return qrCodeForChatie()
+ }
+ }
+ fun getAvatar(): FileBox {
+ try {
+ return wechaty.getPuppet().getContactAvatar(this.id).get()
+ } catch (e: Exception) {
+ log.error("error",e)
+ return qrCodeForChatie()
}
}
+ fun qrCodeForChatie (): FileBox {
+ val CHATIE_OFFICIAL_ACCOUNT_QRCODE = "http://weixin.qq.com/r/qymXj7DEO_1ErfTs93y5"
+ return FileBox.fromQRCode(CHATIE_OFFICIAL_ACCOUNT_QRCODE)
+ }
+
fun tags():List{
val tagIdList = wechaty.getPuppet().tagContactList(this.id).get()
return try {
@@ -165,8 +222,19 @@ open class Contact(wechaty: Wechaty,val id:String) : Sayable, Accessory(wechaty)
listOf()
}
}
+ fun getTags():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{
+ fun self():Boolean {
val userId = puppet.selfId()
if(StringUtils.isEmpty(userId)){
return false
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 bfb5d22..e1c859f 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/user/ContactSelf.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/ContactSelf.kt
@@ -2,6 +2,8 @@ package io.github.wechaty.user
import io.github.wechaty.Wechaty
import io.github.wechaty.filebox.FileBox
+import io.github.wechaty.utils.QrcodeUtils.Companion.guardQrCodeValue
+import org.slf4j.LoggerFactory
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Future
@@ -11,6 +13,10 @@ class ContactSelf(wechaty: Wechaty, id: String) : Contact(wechaty, id) {
puppet.setContactAvatar(super.id, fileBox)
}
+ fun getAvatar(fileBox: FileBox) {
+ puppet.setContactAvatar(super.id, fileBox)
+ }
+
fun setName(name:String){
puppet.contactSelfName(name).get()
sync()
@@ -26,6 +32,91 @@ class ContactSelf(wechaty: Wechaty, id: String) : Contact(wechaty, id) {
puppet.contactSelfSignature(signature).get()
sync()
}
+ }
+ fun setSignature(signature:String) {
+
+ var puppetId:String? = puppet.selfId()
+
+ let{
+ puppetId != null
+ }.run {
+ puppet.contactSelfSignature(signature).get()
+ sync()
+ }
+ }
+
+ fun qrcode(): Future {
+ log.info("Contact, qrcode()")
+
+ val puppetId: String = try {
+ this.puppet.selfId().toString()
+ }
+ catch (e: Exception) {
+ throw Exception("Can not get qrcode, user might be either not logged in or already logged out")
+ }
+
+ if (this.id !== puppetId) {
+ throw Exception("only can get qrcode for the login userself")
+ }
+
+ return CompletableFuture.supplyAsync {
+ val qrcodeValue = this.puppet.contactSelfQRCode().get()
+ guardQrCodeValue(qrcodeValue)
+ }
+ }
+ fun getQrcode(): Future {
+ log.info("Contact, qrcode()")
+
+ val puppetId: String = try {
+ this.puppet.selfId().toString()
+ }
+ catch (e: Exception) {
+ throw Exception("Can not get qrcode, user might be either not logged in or already logged out")
+ }
+
+ if (this.id !== puppetId) {
+ throw Exception("only can get qrcode for the login userself")
+ }
+
+ return CompletableFuture.supplyAsync {
+ val qrcodeValue = this.puppet.contactSelfQRCode().get()
+ guardQrCodeValue(qrcodeValue)
+ }
+ }
+
+ fun name(name: String?): String? {
+ if (name == null) {
+ return super.name()
+ }
+ val puppetId = try {
+ this.puppet.selfId()
+ } catch (e: Exception) {
+ throw Exception("Can not get qrcode, user might be either not logged in or already logged out")
+ }
+ if (this.id !== puppetId) {
+ throw Exception("only can get qrcode for the login userself")
+ }
+ this.puppet.contactSelfName(name)
+ return null
+ }
+
+ fun getName(name: String?): String? {
+ if (name == null) {
+ return super.name()
+ }
+ val puppetId = try {
+ this.puppet.selfId()
+ } catch (e: Exception) {
+ throw Exception("Can not get qrcode, user might be either not logged in or already logged out")
+ }
+ if (this.id !== puppetId) {
+ throw Exception("only can get qrcode for the login userself")
+ }
+ this.puppet.contactSelfName(name)
+ return null
+ }
+ companion object {
+ private val log = LoggerFactory.getLogger(ContactSelf::class.java)
}
}
diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/Favorite.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/Favorite.kt
index 56f4897..de7d7a3 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/user/Favorite.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/Favorite.kt
@@ -1,10 +1,10 @@
package io.github.wechaty.user
import io.github.wechaty.Accessory
+import io.github.wechaty.Puppet
import io.github.wechaty.Wechaty
+import io.github.wechaty.schemas.ContactPayload
class Favorite(wechaty: Wechaty):Accessory(wechaty){
-
-
-}
\ No newline at end of file
+}
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 8afcb75..cfbf567 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/user/Friendship.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/Friendship.kt
@@ -14,6 +14,22 @@ class Friendship (wechaty: Wechaty,val id:String):Accessory(wechaty){
private var payload:FriendshipPayload? = null
+ 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
}
@@ -24,7 +40,6 @@ class Friendship (wechaty: Wechaty,val id:String):Accessory(wechaty){
}
this.payload = wechaty.getPuppet().friendshipPayload(id!!).get()
contact().ready()
-
}
fun contact():Contact{
@@ -55,17 +70,20 @@ class Friendship (wechaty: Wechaty,val id:String):Accessory(wechaty){
return this.payload?.hello ?: "";
}
- fun type():FriendshipType{
+ fun type(): FriendshipType {
return this.payload?.type ?:FriendshipType.Unknown
}
+ fun getType(): FriendshipType {
+ return this.payload?.type ?: throw Exception("ne payload")
+ }
+
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/Message.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/Message.kt
index 7dd15c9..f1b30ee 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/user/Message.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/Message.kt
@@ -7,9 +7,11 @@ import io.github.wechaty.schemas.MessagePayload
import io.github.wechaty.schemas.MessageType
import io.github.wechaty.schemas.RoomMemberQueryFilter
import io.github.wechaty.type.Sayable
+import io.grpc.netty.shaded.io.netty.util.concurrent.CompleteFuture
import org.apache.commons.collections4.CollectionUtils
import org.apache.commons.lang3.StringUtils
import org.slf4j.LoggerFactory
+import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Future
import java.util.regex.Pattern
@@ -116,6 +118,9 @@ open class Message(wechaty: Wechaty,val id: String) : Sayable, Accessory(wechaty
}
return this.payload?.type ?: MessageType.Unknown
}
+ fun getType(): MessageType{
+ return this.payload?.type ?: throw Exception("no payload")
+ }
fun self():Boolean{
val selfId = puppet.selfId()
@@ -124,6 +129,45 @@ open class Message(wechaty: Wechaty,val id: String) : Sayable, Accessory(wechaty
return StringUtils.equals(selfId,from?.id)
}
+ fun date(): Date {
+ val payload = wechaty.getPuppet().messagePayload(this.id).get()
+ return Date(payload.timestamp!! * 1000)
+ }
+
+ fun getDate(): Date {
+ val payload = wechaty.getPuppet().messagePayload(this.id).get()
+ return Date(payload.timestamp!! * 1000)
+ }
+
+ fun age(): Long {
+ val ageMilliseconds = Date().time - this.date().time
+ val ageSeconds = Math.floor(ageMilliseconds / 1000.0).toLong()
+ return ageSeconds
+ }
+
+ fun getAge(): Long {
+ val ageMilliseconds = Date().time - this.date().time
+ val ageSeconds = Math.floor(ageMilliseconds / 1000.0).toLong()
+ return ageSeconds
+ }
+
+ fun forward(to: Any): Future {
+ log.debug("Message, forward({})", to)
+ when(to) {
+ is Room -> {
+ this.puppet.messageForward(to.id, this.id).get()
+ }
+ is Contact -> {
+ this.puppet.messageForward(to.id, this.id).get()
+ }
+ else -> {
+ throw Exception("unkown forward type")
+ }
+ }
+ return CompletableFuture.completedFuture(null)
+ }
+
+
fun mentionList():List{
val room = this.room()
@@ -171,7 +215,6 @@ open class Message(wechaty: Wechaty,val id: String) : Sayable, Accessory(wechaty
return text()
}
-
fun ready():Future{
return CompletableFuture.supplyAsync {
diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/MiniProgram.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/MiniProgram.kt
index c5217cb..8243a20 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/user/MiniProgram.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/MiniProgram.kt
@@ -35,11 +35,10 @@ class MiniProgram(var payload: MiniProgramPayload) {
companion object{
- fun create():MiniProgram{
+ fun create(): MiniProgram {
val payload = MiniProgramPayload()
return MiniProgram(payload);
-
}
}
-}
\ No newline at end of file
+}
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 9ed5058..35d94c5 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/user/Room.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/Room.kt
@@ -15,7 +15,10 @@ import io.github.wechaty.io.github.wechaty.schemas.EventEnum
import io.github.wechaty.schemas.RoomMemberQueryFilter
import io.github.wechaty.schemas.RoomPayload
import io.github.wechaty.type.Sayable
+import io.github.wechaty.user.manager.RoomManager
+import io.github.wechaty.utils.JsonUtils
import io.github.wechaty.utils.QrcodeUtils
+import io.grpc.netty.shaded.io.netty.util.concurrent.CompleteFuture
import org.apache.commons.collections4.CollectionUtils
import org.apache.commons.lang3.StringUtils
import org.slf4j.LoggerFactory
@@ -57,9 +60,7 @@ class Room(wechaty: Wechaty, val id: String) : Accessory(wechaty), Sayable {
message.ready().get()
return@supplyAsync message
}
-
return@supplyAsync null
-
}
}
@@ -349,13 +350,41 @@ class Room(wechaty: Wechaty, val id: String) : Accessory(wechaty), Sayable {
}
}
+ fun getAnnounce(): Future {
+ return CompletableFuture.supplyAsync {
+ puppet.getRoomAnnounce(id).get()
+ }
+ }
+
+ fun setAnnounce(text: String): Future {
+ return puppet.setRoomAnnounce(id, text)
+ }
+
fun qrCode(): Future {
return CompletableFuture.supplyAsync {
val qrCodeValue = puppet.roomQRCode(id).get()
return@supplyAsync QrcodeUtils.guardQrCodeValue(qrCodeValue)
}
}
+ fun getQrCode(): Future {
+ return CompletableFuture.supplyAsync {
+ val qrCodeValue = puppet.roomQRCode(id).get()
+ return@supplyAsync QrcodeUtils.guardQrCodeValue(qrCodeValue)
+ }
+ }
+ fun member(query: RoomMemberQueryFilter?): Contact? {
+ val memberList = memberAll(query)
+
+ if (memberList == null || memberList.size == 0) {
+ return null
+ }
+ if (memberList.size > 1) {
+ log.warn("Room, member({}) get {} contacts, use the first one by default", query?.let { JsonUtils.write(it) }, memberList.size)
+ }
+
+ return memberList[0];
+ }
fun memberAll(query: RoomMemberQueryFilter?): List {
if (query == null) {
@@ -364,11 +393,10 @@ class Room(wechaty: Wechaty, val id: String) : Accessory(wechaty), Sayable {
val contactIdList = wechaty.getPuppet().roomMemberSearch(this.id, query).get()
val contactList = contactIdList.map {
- wechaty.contactManager.load(id)
+ id -> wechaty.contactManager.load(id)
}
return contactList
-
}
fun memberList(): List {
@@ -380,19 +408,15 @@ class Room(wechaty: Wechaty, val id: String) : Accessory(wechaty), Sayable {
}
val contactList = memberIdList.map {
- wechaty.contactManager.load(id)
+ id -> wechaty.contactManager.load(id)
}
return contactList
}
fun alias(contact: Contact): String? {
-
val roomMemberPayload = wechaty.getPuppet().roomMemberPayload(this.id, contact.id).get()
-
return roomMemberPayload?.roomAlias
-
-
}
fun has(contact: Contact): Boolean {
@@ -422,6 +446,11 @@ class Room(wechaty: Wechaty, val id: String) : Accessory(wechaty), Sayable {
return puppet.roomAvatar(this.id).get()
}
+ fun getAvatar(): FileBox {
+ log.debug("getAvatar:{}", getAvatar())
+ return puppet.roomAvatar(this.id).get()
+ }
+
companion object {
private val log = LoggerFactory.getLogger(Room::class.java)
}
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 779ecb7..5ef4ec9 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/user/RoomInvitation.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/RoomInvitation.kt
@@ -52,25 +52,41 @@ class RoomInvitation(wechaty: Wechaty,val id:String) : Accessory(wechaty){
return Date(payload.timestamp!! * 1000)
}
+ fun getDate(): Date {
+ val payload = wechaty.getPuppet().roomInvitationPayload(this.id).get()
+ return Date(payload.timestamp!! * 1000)
+ }
fun age():Long{
val recvDate = this.date()
return System.currentTimeMillis() - recvDate.time;
}
+ fun getAge():Long{
+ val recvDate = this.date()
+ return System.currentTimeMillis() - recvDate.time;
+ }
fun inviter():Contact{
val payload = wechaty.getPuppet().roomInvitationPayload(this.id).get()
return wechaty.contactManager.load(payload.inviterId!!)
}
+ fun getInviter():Contact{
+ val payload = wechaty.getPuppet().roomInvitationPayload(this.id).get()
+ return wechaty.contactManager.load(payload.inviterId!!)
+ }
+
fun topic():String {
val payload = wechaty.getPuppet().roomInvitationPayload(this.id).get()
return payload.topic ?:""
}
+ fun getTopic():String {
+ val payload = wechaty.getPuppet().roomInvitationPayload(this.id).get()
+ return payload.topic ?:""
+ }
+
companion object{
private val log = LoggerFactory.getLogger(RoomInvitation::class.java)
}
-
-
}
diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/Tag.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/Tag.kt
index bd8af36..29bda65 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/user/Tag.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/Tag.kt
@@ -14,7 +14,6 @@ class Tag(wechaty:Wechaty,val id:String):Accessory(wechaty){
wechaty.getPuppet().tagContactRemove(this.id!!,from.id!!).get()
}
-
companion object{
private val log = LoggerFactory.getLogger(Tag::class.java)
}
diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/UrlLink.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/UrlLink.kt
index 1024566..ff7d99d 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/user/UrlLink.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/UrlLink.kt
@@ -69,7 +69,6 @@ class UrlLink(val payload:UrlLinkPayload) {
return UrlLink(payload)
-//
}
}
@@ -79,7 +78,13 @@ class UrlLink(val payload:UrlLinkPayload) {
fun main(){
val create = UrlLink.create("https://xilidou.com")
-
+// val bilibili = UrlLink.create("https://www.bilibili.com/")
+// val image = UrlLink.create("https://img.xilidou.com/img/java-wechaty.png")
println(create)
-
+// println(bilibili)
+// print(image)
+ val urlLink = UrlLinkPayload("Nihao", "https://www.bilibili.com/")
+ urlLink.thumbnailUrl = "https://xilidou.com/images/avatar.jpg"
+ urlLink.description = "犀利豆的博客"
+ print(urlLink)
}
diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/manager/ContactManager.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/ContactManager.kt
index b6ccb8c..33e2046 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/user/manager/ContactManager.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/ContactManager.kt
@@ -30,9 +30,6 @@ class ContactManager(wechaty: Wechaty):Accessory(wechaty) {
}
-
-
-
fun find(queryFilter: ContactQueryFilter):Contact?{
val findAll = findAll(queryFilter)
@@ -72,7 +69,7 @@ class ContactManager(wechaty: Wechaty):Accessory(wechaty) {
}
companion object {
- private val log = LoggerFactory.getLogger(Contact::class.java)
+ private val log = LoggerFactory.getLogger(ContactManager::class.java)
}
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..4a01f8c
--- /dev/null
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/FriendShipManager.kt
@@ -0,0 +1,49 @@
+package io.github.wechaty.user.manager
+
+import com.github.benmanes.caffeine.cache.Cache
+import com.github.benmanes.caffeine.cache.Caffeine
+import io.github.wechaty.Accessory
+import io.github.wechaty.Wechaty
+import io.github.wechaty.schemas.*
+import io.github.wechaty.user.Contact
+import io.github.wechaty.user.ContactSelf
+import io.github.wechaty.user.Friendship
+import io.github.wechaty.user.Tag
+import org.apache.commons.collections4.CollectionUtils
+import org.apache.commons.lang3.StringUtils
+import org.slf4j.LoggerFactory
+import java.util.*
+
+class FriendShipManager(wechaty: Wechaty):Accessory(wechaty) {
+
+ private val friendshipCache: Cache = Caffeine.newBuilder().build()
+
+ fun load(id:String): Friendship {
+ return friendshipCache.get(id) {
+ Friendship(wechaty, id)
+ }!!
+ }
+
+ // 查找发送请求的好友
+ fun search(queryFilter: FriendshipSearchCondition): Contact? {
+ val friendshipId = wechaty.getPuppet().friendshipSearch(queryFilter).get();
+ if(StringUtils.isEmpty(friendshipId)){
+ return null
+ }
+ val contact = wechaty.contactManager.load(friendshipId!!)
+ contact.ready()
+ return contact
+ }
+
+ // 添加好友
+ fun add(contact: Contact, hello:String) {
+ log.debug("add contact: {} hello: {}", contact, hello)
+ wechaty.getPuppet().friendshipAdd(contact.id, hello).get()
+ }
+
+ companion object {
+ private val log = LoggerFactory.getLogger(FriendShipManager::class.java)
+ }
+
+
+}
diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/manager/MessageManager.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/MessageManager.kt
index e9dc3c9..f4208ee 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/user/manager/MessageManager.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/MessageManager.kt
@@ -56,7 +56,7 @@ class MessageManager (wechaty: Wechaty):Accessory(wechaty){
}
companion object {
- private val log = LoggerFactory.getLogger(Contact::class.java)
+ private val log = LoggerFactory.getLogger(MessageManager::class.java)
}
diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/manager/RoomInvitationManager.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/RoomInvitationManager.kt
index a2b514d..eabed38 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/user/manager/RoomInvitationManager.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/RoomInvitationManager.kt
@@ -9,13 +9,12 @@ import org.slf4j.LoggerFactory
class RoomInvitationManager (wechaty: Wechaty):Accessory(wechaty){
- fun load(id:String):RoomInvitation{
+ fun load(id:String): RoomInvitation {
return RoomInvitation(wechaty,id)
}
-
companion object {
- private val log = LoggerFactory.getLogger(Contact::class.java)
+ private val log = LoggerFactory.getLogger(RoomInvitationManager::class.java)
}
}
diff --git a/wechaty/src/main/kotlin/io/github/wechaty/user/manager/TagManager.kt b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/TagManager.kt
index 02435da..1ec3700 100644
--- a/wechaty/src/main/kotlin/io/github/wechaty/user/manager/TagManager.kt
+++ b/wechaty/src/main/kotlin/io/github/wechaty/user/manager/TagManager.kt
@@ -18,7 +18,7 @@ class TagManager(wechaty: Wechaty):Accessory(wechaty){
}!!
}
- fun get(id:String):Tag{
+ fun get(id:String): Tag{
return load(id)
}
@@ -26,6 +26,13 @@ class TagManager(wechaty: Wechaty):Accessory(wechaty){
wechaty.getPuppet().tagContactDelete(tag.id)
}
+ fun tags():List{
+ val tagIdList = wechaty.getPuppet().tagContactList().get()
+ return tagIdList.map {
+ wechaty.tagManager.load(it)
+ }
+ }
+
companion object{
private val log = LoggerFactory.getLogger(TagManager::class.java)
}
diff --git a/wechaty/src/main/resources/log4j2.xml b/wechaty/src/main/resources/log4j2.xml
index 0e80944..b7f6060 100644
--- a/wechaty/src/main/resources/log4j2.xml
+++ b/wechaty/src/main/resources/log4j2.xml
@@ -11,7 +11,6 @@
-