├── .all-contributorsrc
├── .gitignore
├── README.md
├── build.gradle.kts
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
└── src
└── main
├── kotlin
└── com
│ └── hcyacg
│ └── protocol
│ ├── anno
│ └── NoArg.kt
│ ├── common
│ ├── BotApi.kt
│ ├── BotClient.kt
│ ├── BotEvent.kt
│ ├── BotListener.kt
│ ├── BotManager.kt
│ ├── ExcepetionHandler.kt
│ ├── Gateway.kt
│ ├── MonitorMessage.kt
│ └── SequelBotClient.kt
│ ├── constant
│ └── Constant.kt
│ ├── entity
│ ├── Announces.kt
│ ├── AudioControl.kt
│ ├── Channel.kt
│ ├── ChannelPermissions.kt
│ ├── Guild.kt
│ ├── Member.kt
│ ├── Message.kt
│ ├── MessageAudited.kt
│ ├── Role.kt
│ ├── Roles.kt
│ ├── Schedule.kt
│ └── User.kt
│ ├── event
│ ├── AudioActionEvent.kt
│ ├── ChannelEvent.kt
│ ├── GuildEvent.kt
│ ├── GuildMemberEvent.kt
│ ├── ReadyEvent.kt
│ ├── api
│ │ ├── Author.kt
│ │ ├── Dms.kt
│ │ ├── Member.kt
│ │ ├── Message.kt
│ │ └── User.kt
│ └── message
│ │ ├── AtMessageCreateEvent.kt
│ │ ├── EventApi.kt
│ │ ├── MessageAuditPassEvent.kt
│ │ ├── MessageAuditRejectEvent.kt
│ │ ├── MessageCreateEvent.kt
│ │ ├── MessageEvent.kt
│ │ ├── MessageReactionEvent.kt
│ │ └── directMessage
│ │ ├── DirectMessage.kt
│ │ ├── DirectMessageCreateEvent.kt
│ │ └── DmsApi.kt
│ ├── internal
│ ├── BaseBotClient.kt
│ ├── BaseBotListener.kt
│ ├── config
│ │ └── IdentifyConfig.kt
│ ├── entity
│ │ ├── AccessWithFragmentedWss.kt
│ │ ├── Dispatch.kt
│ │ ├── Heartbeat.kt
│ │ ├── Identify.kt
│ │ ├── Operation.kt
│ │ ├── Resume.kt
│ │ └── UniversalWssAccess.kt
│ └── enums
│ │ ├── DispatchEnums.kt
│ │ └── OPCodeEnums.kt
│ └── utils
│ ├── OkHttpUtils.kt
│ ├── ScheduleUtils.kt
│ └── UA.kt
└── resources
└── logback.xml
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "contributors": [
8 | {
9 | "login": "Nekoer",
10 | "name": "牧瀬くりす",
11 | "avatar_url": "https://avatars.githubusercontent.com/u/32485369?v=4",
12 | "profile": "https://www.hcyacg.com/",
13 | "contributions": [
14 | "code",
15 | "doc"
16 | ]
17 | },
18 | {
19 | "login": "cssxsh",
20 | "name": "cssxsh",
21 | "avatar_url": "https://avatars.githubusercontent.com/u/32539286?v=4",
22 | "profile": "https://github.com/cssxsh",
23 | "contributions": [
24 | "review"
25 | ]
26 | }
27 | ],
28 | "contributorsPerLine": 7,
29 | "projectName": "tencent-guild-protocol",
30 | "projectOwner": "Nekoer",
31 | "repoType": "github",
32 | "repoHost": "https://github.com",
33 | "skipCi": true
34 | }
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # 项目排除路径
2 | /.gradle/
3 | /build/
4 | *.properties
5 | *.rev
6 | *.gpg
7 | *.log
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tencent-guild-protocol
2 |
3 | [](#contributors-)
4 |
5 |
6 | > Tencent频道的机器人Kotlin SDK
7 |
8 | [](https://search.maven.org/artifact/com.hcyacg/tencent-guild-protocol)
9 |
10 | ## 导入依赖
11 |
12 | ### Maven
13 |
14 | ```Maven
15 |
16 | com.hcyacg
17 | tencent-guild-protocol
18 | ${version}
19 |
20 | ```
21 |
22 | ### Gradle Groovy DSL
23 |
24 | ```Gradle Groovy DSL
25 | implementation 'com.hcyacg:tencent-guild-protocol:${version}'
26 | ```
27 |
28 | ### Gradle Kotlin DSL
29 |
30 | ```Gradle Kotlin DSL
31 | implementation("com.hcyacg:tencent-guild-protocol:${version}")
32 | ```
33 |
34 | ## 如何使用
35 |
36 | 1. 首先配置你的botId 和 botToken
37 |
38 | ```kotlin
39 | fun main(args: Array) {
40 | val token = "Bot id.token"
41 | //放入你的Listener
42 | //默认是获取公域的信息
43 | BotManager.configuration(token, false).addListen(Test())
44 | //私域请使用
45 | BotManager.configuration(token,true).addListen(Test())
46 | }
47 |
48 | ```
49 |
50 | 2. 实现你的Listener ,这里会附赠给你一个例子 :`Test`
51 |
52 | ```kotlin
53 | //实际上实现一个Listener十分简单,你只需要继承`BotEvent`,然后实现它里面你需要的方法就可以了
54 | class Test : BotEvent() {
55 |
56 | //这里我实现了 私域中 事件的 监听
57 | override suspend fun onMessageCreate(event: MessageCreateEvent) {
58 |
59 | }
60 | }
61 | ```
62 |
63 | 3.SDK内置了目前官方已出的功能
64 |
65 | ```kotlin
66 | //获取子频道消息
67 | BotApi.getChannelInfo(data.channel_id)
68 | //获取频道下的子频道列表
69 | BotApi.getChildChannelInfo(data.guild_id)
70 | //获取单个成员消息
71 | BotApi.getMemberInfo(data.guild_id, data.author.id)
72 | //获取频道信息
73 | BotApi.getGuildById(data.guild_id)
74 | //获取频道身份组列表
75 | BotApi.getRolesByGuild(data.guild_id)
76 | //创建频道身份组
77 | BotApi.createRolesByGuild(data.guild_id, Filter(1, 1, 0), Info("测试", 16757760, 0))
78 | //修改频道身份组
79 | BotApi.changeRolesByGuild(data.guild_id, "", Filter(1, 1, 0), Info("测试", 16758465, 0))
80 | //删除频道身份组
81 | BotApi.deleteRolesByGuild(data.guild_id, "")
82 | //增加频道身份组成员(除子频道管理员
83 | BotApi.createMemberRolesByGuild(data.guild_id, "", "")
84 | //增加频道身份组成员(仅子频道管理员
85 | BotApi.createChildMemberRolesByGuild(data.guild_id, BotApi.getChannelInfo(data.channel_id), "", "")
86 | //删除频道身份组成员(除子频道管理员
87 | BotApi.deleteMemberRolesByGuild(data.guild_id, "", "")
88 | //删除频道身份组成员(仅子频道管理员
89 | BotApi.deleteChildMemberRolesByGuild(data.guild_id, BotApi.getChannelInfo(data.channel_id), "", "")
90 | //修改指定子频道的权限 目前只支持修改查看权限
91 | BotApi.changeChannelPermissions(data.channel_id, "", false)
92 | //创建子频道公告
93 | BotApi.createAnnounces(data.channel_id, data.id)
94 | //删除子频道公告
95 | BotApi.deleteAnnounces(data.channel_id, message_id)
96 | //获得机器人信息
97 | BotApi.getMe()
98 | //获取当前用户频道列表
99 | BotApi.getMeGuildsAfter(data.guild_id, 100)
100 | BotApi.getMeGuildsBefore(data.guild_id, 100)
101 |
102 | //获取日程列表
103 | BotApi.getScheduleList(日程子频道id)
104 | //创建日程
105 | BotApi.createSchedule(日程子频道id, 日程对象)
106 | //修改日程
107 | BotApi.changeScheduleById(日程子频道id, 日程id, 日程对象)
108 | //根据日程id删除日程
109 | BotApi.deleteScheduleById(日程子频道id, 日程id)
110 | //返回结束时间在时间戳之后的日程列表
111 | BotApi.getScheduleListByTime(日程子频道id, 时间戳)
112 |
113 | ```
114 |
115 | ### 私域功能
116 |
117 | ```Kotlin
118 | //获取频道成员列表
119 | BotApi.getGuildMemberList(data.guild_id, "0", 1)
120 | //创建子频道
121 | BotApi.createChannel(data.guild_id, ChannelDto("测试", ChannelType.textSubchannel, 排序id, "父类节点"))
122 | //修改子频道信息
123 | BotApi.changeChannelInfo(data.channel_id, ChannelDto("测试", ChannelType.textSubchannel, 排序id, "父类节点"))
124 | //删除子频道
125 | BotApi.deleteChannel(data.channel_id)
126 | //删除指定频道成员
127 | BotApi.deleteMember(data.guild_id, "用户id")
128 | ```
129 |
130 | 4. AtMessageCreateEvent 和 MessageCreateEvent 都存在回复功能
131 |
132 | ```kotlin
133 | //被动
134 | event.replyArk()
135 | event.replyImage()
136 | event.replyEmbed()
137 | event.replyText()
138 | event.replyAudio()
139 | event.replyTextWithImage()
140 | //主动消息
141 | event.replyArkNotId()
142 | event.replyImageNotId()
143 | event.replyEmbedNotId()
144 | event.replyTextNotId()
145 | event.replyTextWithImageNotId()
146 | //禁言
147 | event.mute(120)
148 | event.author.mute(120)
149 |
150 | //ark模板例子
151 | event.replyArk(
152 | MessageArk(
153 | 23, listOf(
154 | MessageArkKv("#DESC#", "descaaaaaa", null),
155 | MessageArkKv("#PROMPT#", "promptaaaa", null),
156 | MessageArkKv(
157 | "#LIST#", null, listOf(
158 | MessageArkObj(listOf(MessageArkObjKv("desc", "你好"))),
159 | MessageArkObj(listOf(MessageArkObjKv("desc", "你好"))),
160 | MessageArkObj(listOf(MessageArkObjKv("desc", "你好"))),
161 | MessageArkObj(listOf(MessageArkObjKv("desc", "你好"))),
162 | MessageArkObj(listOf(MessageArkObjKv("desc", "你好")))
163 | )
164 | )
165 | )
166 | )
167 | )
168 | ```
169 | ## Contributors ✨
170 |
171 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
172 |
173 |
174 |
175 |
176 |
182 |
183 |
184 |
185 |
186 |
187 |
188 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2 |
3 | plugins {
4 | `java-library`
5 | kotlin("jvm") version "1.6.0"
6 | kotlin("plugin.serialization") version "1.6.0"
7 | id("org.jetbrains.kotlin.plugin.noarg") version "1.6.0"
8 | `maven-publish`
9 | signing
10 | id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
11 | }
12 |
13 | group = "com.hcyacg"
14 | version = "0.3.9"
15 |
16 |
17 | repositories {
18 | mavenLocal()
19 | mavenCentral()
20 | }
21 |
22 | dependencies {
23 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.0-native-mt")
24 | // implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1")
25 | implementation("com.squareup.okhttp3:okhttp:4.9.3")
26 |
27 | implementation("org.apache.commons:commons-lang3:3.12.0")
28 | implementation("org.apache.httpcomponents:httpclient:4.5.13")
29 | implementation("org.jsoup:jsoup:1.14.3")
30 | implementation("org.slf4j:slf4j-api:1.7.33")
31 | implementation("ch.qos.logback:logback-core:1.2.10")
32 | implementation("ch.qos.logback:logback-classic:1.2.10")
33 | implementation("com.google.code.gson:gson:2.8.9")
34 |
35 | testImplementation("org.jetbrains.kotlin:kotlin-test:1.6.0")
36 | }
37 |
38 | tasks.test {
39 | useJUnit()
40 | }
41 |
42 | tasks.withType() {
43 | kotlinOptions.jvmTarget = "11"
44 | }
45 |
46 | noArg {
47 | annotation("com.lindroid.projectname.annotation.NoArg")
48 | }
49 |
50 | java {
51 | withJavadocJar()
52 | withSourcesJar()
53 | }
54 |
55 | tasks.register("stuffZip") {
56 | archiveBaseName.set("stuff")
57 | from("src/stuff")
58 | }
59 |
60 | publishing {
61 | publications {
62 | create("mavenJava") {
63 | artifactId = rootProject.name
64 | from(components["java"])
65 | versionMapping {
66 | usage("java-api") {
67 | fromResolutionOf("runtimeClasspath")
68 | }
69 | usage("java-runtime") {
70 | fromResolutionResult()
71 | }
72 | }
73 | pom {
74 | name.set(rootProject.name)
75 | description.set("The Robot Kotlin SDK on Tencent Channel")
76 | url.set("https://github.com/Nekoer/tencent-guild-protocol")
77 | // properties.set(mapOf(
78 | // "myProp" to "value",
79 | // "prop.with.dots" to "anotherValue"
80 | // ))
81 | licenses {
82 | license {
83 | name.set("The Apache License, Version 2.0")
84 | url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
85 | }
86 | }
87 | developers {
88 | developer {
89 | id.set("Nekoer")
90 | name.set("Nekoer")
91 | email.set("hcyacg@vip.qq.com")
92 | }
93 | }
94 | scm {
95 | connection.set("scm:git:git://github.com/Nekoer/tencent-guild-protocol.git")
96 | developerConnection.set("scm:git:ssh://github.com/Nekoer/tencent-guild-protocol.git")
97 | url.set("https://github.com/Nekoer/tencent-guild-protocol")
98 | }
99 | }
100 | }
101 | }
102 | repositories {
103 | maven {
104 | // change URLs to point to your repos, e.g. http://my.org/repo
105 | val releasesRepoUrl = uri(layout.buildDirectory.dir("repos/releases"))
106 | val snapshotsRepoUrl = uri(layout.buildDirectory.dir("repos/snapshots"))
107 | url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
108 | }
109 | }
110 | }
111 |
112 | signing {
113 | sign(publishing.publications["mavenJava"])
114 | }
115 |
116 | tasks.javadoc {
117 | if (JavaVersion.current().isJava9Compatible) {
118 | (options as StandardJavadocDocletOptions).addBooleanOption("html5", true)
119 | }
120 | }
121 |
122 | nexusPublishing {
123 | repositories {
124 | sonatype { //only for users registered in Sonatype after 24 Feb 2021
125 | nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
126 | snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
127 |
128 | username.set(properties["myNexusTokenUsername"].toString())
129 | password.set(properties["myNexusTokenPassword"].toString())
130 | }
131 | }
132 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MSYS* | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 |
2 | rootProject.name = "tencent-guild-protocol"
3 |
4 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/anno/NoArg.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.anno
2 |
3 | annotation class NoArg()
4 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/common/BotApi.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.common
2 |
3 | import com.google.gson.Gson
4 | import com.google.gson.reflect.TypeToken
5 | import com.hcyacg.protocol.constant.Constant.Companion.botToken
6 | import com.hcyacg.protocol.constant.Constant.Companion.logger
7 | import com.hcyacg.protocol.constant.Constant.Companion.proUrl
8 | import com.hcyacg.protocol.entity.*
9 | import com.hcyacg.protocol.entity.Member
10 | import com.hcyacg.protocol.entity.Message
11 | import com.hcyacg.protocol.entity.User
12 | import com.hcyacg.protocol.utils.OkHttpUtils
13 | import okhttp3.Headers
14 |
15 | object BotApi {
16 |
17 | private const val userMe = "$proUrl/users/@me"
18 | private const val userMeGuild = "$userMe/guilds"
19 |
20 | private const val channel = "$proUrl/channels/{{channel_id}}"
21 | private const val channelPermissions = "$channel/members/{{user_id}}/permissions"
22 | private const val sendMessage = "$channel/messages"
23 | private const val getMessage = "$channel/messages/{{message_id}}"
24 | private const val sendAudio = "$channel/audio"
25 | private const val recall = "$sendMessage/{{message_id}}"
26 |
27 | private const val channelRolePermissions = "$channel/roles/{{role_id}}/permissions"
28 |
29 | private const val guildInfo = "$proUrl/guilds/{{guild_id}}"
30 | private const val channelList = "$guildInfo/channels"
31 | private const val memberInfo = "$guildInfo/members/{{user_id}}"
32 | private const val memberMute = "$guildInfo/members/{{user_id}}/mute"
33 |
34 | private const val memberList = "$guildInfo/members"
35 | private const val roles = "$guildInfo/roles"
36 | private const val changeRole = "$guildInfo/roles/{{role_id}}"
37 | private const val editMemberRole = "$guildInfo/members/{{user_id}}/roles/{{role_id}}"
38 |
39 | private const val guildAnnounce = "$proUrl/guilds/{{guild_id}}/announces"
40 | private const val deleteGuildAnnounce = "$guildAnnounce/{{message_id}}"
41 | private const val channelAnnounces = "$proUrl/channels/{{channel_id}}/announces"
42 | private const val deleteChannelAnnounces = "$channelAnnounces/{{message_id}}"
43 |
44 | private const val schedules = "$proUrl/channels/{{channel_id}}/schedules"
45 | private const val scheduleUrl = "$schedules/{{schedule_id}}"
46 |
47 | private const val mute = "$proUrl/guilds/{{guild_id}}/mute"
48 |
49 |
50 | private fun officeApiHeader(): MutableMap {
51 | return mutableMapOf(
52 | "Authorization" to botToken!!
53 | )
54 | }
55 |
56 |
57 | /**
58 | * 获取频道信息
59 | * @param guildId 频道id
60 | */
61 | @JvmStatic
62 | fun getGuildById(guildId: String): Guild {
63 |
64 |
65 | val url = guildInfo.replace("{{guild_id}}", guildId)
66 | val res = OkHttpUtils[url, officeApiHeader()]
67 |
68 | val result = res.body?.string()
69 | logger.debug(result)
70 | return Gson().fromJson(result, Guild::class.java)
71 | }
72 |
73 |
74 | /**
75 | * 获取频道身份组列表
76 | * @param guildId 频道id
77 | */
78 | @JvmStatic
79 | fun getRolesByGuild(guildId: String): Roles {
80 | val url = roles.replace("{{guild_id}}", guildId)
81 | val res = OkHttpUtils.getJson(url, officeApiHeader())
82 | logger.debug(res)
83 | return Gson().fromJson(res, Roles::class.java)
84 | }
85 |
86 | /**
87 | * 创建频道身份组
88 | * @param guildId 频道id
89 | * @param filter 标识需要修改哪些字段
90 | * @param info 携带需要修改的字段内容
91 | */
92 | @JvmStatic
93 | fun createRolesByGuild(guildId: String, filter: Filter, info: Info): RoleVo {
94 | val url = roles.replace("{{guild_id}}", guildId)
95 |
96 | val json = Gson().toJson(RoleDto(filter, info))
97 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
98 | logger.debug(res)
99 | return Gson().fromJson(res, RoleVo::class.java)
100 | }
101 |
102 | /**
103 | * 修改频道身份组
104 | * @param guildId 频道id
105 | * @param roleId 身份组id
106 | * @param filter 标识需要修改哪些字段
107 | * @param info 携带需要修改的字段内容
108 | */
109 | @JvmStatic
110 | fun changeRolesByGuild(guildId: String, roleId: String, filter: Filter, info: Info): RoleVo {
111 | val url = changeRole.replace("{{guild_id}}", guildId).replace("{{role_id}}", roleId)
112 | val json = Gson().toJson(RoleDto(filter, info))
113 | val res = OkHttpUtils.patchJson(url, OkHttpUtils.addJson(json), officeApiHeader())
114 | logger.debug(res)
115 | return Gson().fromJson(res, RoleVo::class.java)
116 | }
117 |
118 | /**
119 | * 删除频道身份组
120 | * @param guildId 频道id
121 | * @param roleId 身份组id
122 | */
123 | @JvmStatic
124 | fun deleteRolesByGuild(guildId: String, roleId: String): Boolean {
125 | val url = changeRole.replace("{{guild_id}}", guildId).replace("{{role_id}}", roleId)
126 |
127 | val res = OkHttpUtils.delete(url, mutableMapOf(), officeApiHeader())
128 | logger.debug(res.body!!.string())
129 | return res.code == 204
130 | }
131 |
132 |
133 | /**
134 | * 增加频道身份组成员(除子频道管理员
135 | * @param guildId 频道id
136 | * @param userId 用户id
137 | * @param roleId 身份组id
138 | */
139 | @JvmStatic
140 | fun createMemberRolesByGuild(guildId: String, userId: String, roleId: String): Boolean {
141 | val url = editMemberRole.replace("{{guild_id}}", guildId).replace("{{user_id}}", userId)
142 | .replace("{{role_id}}", roleId)
143 | val res = OkHttpUtils.put(url, mutableMapOf(), officeApiHeader())
144 | logger.debug(res.code.toString())
145 | return res.code == 204
146 | }
147 |
148 | /**
149 | * 增加频道身份组成员(仅子频道管理员
150 | * @param guildId 频道id
151 | * @param channel Channel对象
152 | * @param userId 用户id
153 | * @param roleId 身份组id
154 | */
155 | @JvmStatic
156 | fun createChildMemberRolesByGuild(guildId: String, channel: Channel, userId: String, roleId: String): Boolean {
157 | val url = editMemberRole.replace("{{guild_id}}", guildId).replace("{{user_id}}", userId)
158 | .replace("{{role_id}}", roleId)
159 | val json = Gson().toJson(channel)
160 | val res = OkHttpUtils.put(url, OkHttpUtils.addJson(json), Headers.headersOf("Authorization", botToken!!))
161 | logger.debug(res.code.toString())
162 | return res.code == 204
163 | }
164 |
165 | /**
166 | * 删除频道身份组成员(除子频道管理员
167 | * @param guildId 频道id
168 | * @param userId 用户id
169 | * @param roleId 身份组id
170 | */
171 | @JvmStatic
172 | fun deleteMemberRolesByGuild(guildId: String, userId: String, roleId: String): Boolean {
173 | val url = editMemberRole.replace("{{guild_id}}", guildId).replace("{{user_id}}", userId)
174 | .replace("{{role_id}}", roleId)
175 | val res = OkHttpUtils.delete(url, mutableMapOf(), officeApiHeader())
176 | logger.debug(res.code.toString())
177 | return res.code == 204
178 | }
179 |
180 | /**
181 | * 删除频道身份组成员(仅子频道管理员
182 | * @param guildId 频道id
183 | * @param channel Channel对象
184 | * @param userId 用户id
185 | * @param roleId 身份组id
186 | */
187 | @JvmStatic
188 | fun deleteChildMemberRolesByGuild(guildId: String, channel: Channel, userId: String, roleId: String): Boolean {
189 | val url = editMemberRole.replace("{{guild_id}}", guildId).replace("{{user_id}}", userId)
190 | .replace("{{role_id}}", roleId)
191 | val json = Gson().toJson(channel)
192 | val res = OkHttpUtils.delete(url, OkHttpUtils.addJson(json), officeApiHeader())
193 | logger.debug(res.code.toString())
194 | return res.code == 204
195 | }
196 |
197 | /**
198 | * 获取单个成员消息
199 | * @param guildId 频道id
200 | * @param userId 用户id
201 | */
202 | @JvmStatic
203 | fun getMemberInfo(guildId: String, userId: String): Member {
204 | val url = memberInfo.replace("{{guild_id}}", guildId).replace("{{user_id}}", userId)
205 |
206 | val res = OkHttpUtils.getJson(url, officeApiHeader())
207 | logger.debug(res)
208 | return Gson().fromJson(res, Member::class.java)
209 | }
210 |
211 | /**
212 | * 获取子频道消息
213 | * @param channelId 子频道id
214 | */
215 | @JvmStatic
216 | fun getChannelInfo(channelId: String): Channel {
217 | val url = channel.replace("{{channel_id}}", channelId)
218 | val res = OkHttpUtils.getJson(url, officeApiHeader())
219 | logger.debug(res)
220 | return Gson().fromJson(res, Channel::class.java)
221 | }
222 |
223 | /**
224 | * 获取频道下的子频道列表
225 | * @param guildId 频道id
226 | */
227 | @JvmStatic
228 | fun getChildChannelInfo(guildId: String): List {
229 | val url = channelList.replace("{{guild_id}}", guildId)
230 |
231 | val res = OkHttpUtils.getJson(url, officeApiHeader())
232 | logger.debug(res)
233 | return Gson().fromJson(res, object : TypeToken>() {}.type)
234 | }
235 |
236 | /**
237 | * 根据message的id获取消息数据
238 | * @param channelId 子频道id
239 | * @param messageId 消息id
240 | */
241 | @JvmStatic
242 | fun getMessageById(channelId: String, messageId: String): Message {
243 | val url = getMessage.replace("{{channel_id}}", channelId).replace("{{message_id}}", messageId)
244 |
245 | val res = OkHttpUtils.getJson(url, officeApiHeader())
246 | logger.debug(res)
247 | return Gson().fromJson(res, Message::class.java)
248 | }
249 |
250 | /**
251 | * 获取指定子频道的权限
252 | * @param channelId 子频道id
253 | * @param userId 用户id
254 | */
255 | @JvmStatic
256 | fun getChannelPermissions(channelId: String, userId: String): ChannelPermissions {
257 | val url = channelPermissions.replace("{{channel_id}}", channelId).replace("{{user_id}}", userId)
258 | val res = OkHttpUtils.getJson(url, officeApiHeader())
259 | logger.debug(res)
260 | return Gson().fromJson(res, ChannelPermissions::class.java)
261 | }
262 |
263 | /**
264 | * 修改指定子频道的权限 目前只支持修改查看权限
265 | * @param channelId 子频道id
266 | * @param userId 用户id
267 | * @param isAdd 是否赋予用户查看子频道权限
268 | */
269 | @JvmStatic
270 | fun changeChannelPermissions(channelId: String, userId: String, isAdd: Boolean): Boolean {
271 | val url = channelPermissions.replace("{{channel_id}}", channelId).replace("{{user_id}}", userId)
272 | val map = mutableMapOf()
273 | if (isAdd) {
274 | map["add"] = 0.shl(1).toString()
275 | } else {
276 | map["remove"] = 1.shl(1).toString()
277 | }
278 | val res = OkHttpUtils.put(url, map, officeApiHeader())
279 | logger.debug(res.code.toString())
280 | return res.code == 204
281 | }
282 |
283 | /**
284 | * 获取指定子频道身份组的权限
285 | * @param channelId 子频道id
286 | * @param roleId 身份组id
287 | */
288 | @JvmStatic
289 | fun getChannelRolePermissions(channelId: String, roleId: String): ChannelPermissions {
290 | val url = channelRolePermissions.replace("{{channel_id}}", channelId).replace("{{role_id}}", roleId)
291 | val res = OkHttpUtils.getJson(url, officeApiHeader())
292 | logger.debug(res)
293 | return Gson().fromJson(res, ChannelPermissions::class.java)
294 | }
295 |
296 | /**
297 | * 修改指定子频道身份组的权限 目前只支持修改查看权限
298 | * @param channelId 子频道id
299 | * @param roleId 身份组id
300 | * @param isAdd 是否赋予用户查看子频道权限
301 | */
302 | @JvmStatic
303 | fun changeChannelRolePermissions(channelId: String, roleId: String, isAdd: Boolean): Boolean {
304 | val url = channelRolePermissions.replace("{{channel_id}}", channelId).replace("{{role_id}}", roleId)
305 | val map = mutableMapOf()
306 | if (isAdd) {
307 | map["add"] = 0.shl(1).toString()
308 | } else {
309 | map["remove"] = 1.shl(1).toString()
310 | }
311 | val res = OkHttpUtils.put(url, map, officeApiHeader())
312 | logger.debug(res.code.toString())
313 | return res.code == 204
314 | }
315 |
316 |
317 | /**
318 | * 获取当前用户信息
319 | */
320 | @JvmStatic
321 | fun getMe(): User {
322 | val res = OkHttpUtils.getJson(userMe, officeApiHeader())
323 | logger.debug(res)
324 | val user = Gson().fromJson(res, User::class.java)
325 | user.bot = true
326 | return user
327 | }
328 |
329 | /**
330 | * 获取当前用户频道列表
331 | * guildId string 读此id之前的数据 guild id, before/after 只能带一个
332 | * limit int 每次拉取多少条数据 最大不超过100,默认100
333 | */
334 | @JvmStatic
335 | fun getMeGuildsBefore(guildId: String, limit: Int): List {
336 | val url = userMeGuild.plus("?before=$guildId").plus("&limit=$limit")
337 | val res = OkHttpUtils.getJson(url, officeApiHeader())
338 | logger.debug(res)
339 | return Gson().fromJson(res, object : TypeToken>() {}.type)
340 | }
341 |
342 | /**
343 | * 获取当前用户频道列表
344 | * guildId string 读此id之后的数据 guild id, before/after 只能带一个
345 | * limit int 每次拉取多少条数据 最大不超过100,默认100
346 | */
347 | @JvmStatic
348 | fun getMeGuildsAfter(guildId: String, limit: Int): List {
349 | val url = userMeGuild.plus("?after=$guildId").plus("&limit=$limit")
350 | val res = OkHttpUtils.getJson(url, officeApiHeader())
351 | logger.debug(res)
352 | return Gson().fromJson(res, object : TypeToken>() {}.type)
353 | }
354 |
355 | /**
356 | * 私域功能
357 | */
358 |
359 | /**
360 | * 获取频道成员列表
361 | * @param guildId 频道id
362 | * @param userId 上一次回包中最大的用户ID, 如果是第一次请求填0,默认为0
363 | * @param limit 分页大小,1-1000,默认是1
364 | */
365 | @JvmStatic
366 | fun getGuildMemberList(guildId: String, userId: String, limit: Int): List {
367 | val url = memberList.replace("{{guild_id}}", guildId)
368 | url.plus("?after=$userId").plus("&limit=$limit")
369 | val res = OkHttpUtils.getJson(url, Headers.headersOf("Authorization", botToken!!))
370 | logger.debug(res)
371 | return Gson().fromJson(res, object : TypeToken>() {}.type)
372 | }
373 |
374 | /**
375 | * 删除指定频道成员
376 | * @param guildId 频道id
377 | * @param userId 用户id
378 | */
379 | @JvmStatic
380 | fun deleteMember(guildId: String, userId: String): Boolean {
381 | val url = memberInfo.replace("{{guild_id}}", guildId).replace("{{user_id}}", userId)
382 | val res = OkHttpUtils.delete(url, mutableMapOf(), officeApiHeader())
383 | logger.debug(res.code.toString())
384 | return res.code == 204
385 | }
386 |
387 | /**
388 | * 创建子频道
389 | * @param guildId 频道id
390 | * @param channelDto 创建子频道需要的参数对象
391 | */
392 | @JvmStatic
393 | fun createChannel(guildId: String, channelDto: ChannelDto): Channel {
394 | val url = channelList.replace("{{guild_id}}", guildId)
395 | val json = Gson().toJson(channelDto)
396 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
397 | logger.debug(res)
398 | return Gson().fromJson(res, Channel::class.java)
399 | }
400 |
401 | /**
402 | * 修改子频道信息
403 | * @param channelId 子频道id
404 | * @param channelDto 创建子频道需要的参数对象
405 | */
406 | @JvmStatic
407 | fun changeChannelInfo(channelId: String, channelDto: ChannelDto): Channel {
408 | val url = channel.replace("{{channel_id}}", channelId)
409 | val json = Gson().toJson(channelDto)
410 | val res = OkHttpUtils.patchJson(url, OkHttpUtils.addJson(json), officeApiHeader())
411 | logger.debug(res)
412 | return Gson().fromJson(res, Channel::class.java)
413 | }
414 |
415 | /**
416 | * 删除子频道
417 | * @param channelId 子频道id
418 | */
419 | @JvmStatic
420 | fun deleteChannel(channelId: String): Boolean {
421 | val url = channel.replace("{{channel_id}}", channelId)
422 | val res = OkHttpUtils.delete(url, mutableMapOf(), officeApiHeader())
423 | logger.debug(res.code.toString())
424 | return res.code == 200
425 | }
426 |
427 |
428 | /**
429 | * 创建全频道公告
430 | * @param guildId 频道id
431 | * @param channelId 子频道id
432 | * @param messageId 消息id
433 | */
434 | @JvmStatic
435 | fun createGuildAnnounces(guildId: String,channelId: String, messageId: String): Announces {
436 | val url = guildAnnounce.replace("{{guild_id}}", guildId)
437 | val json = "{\"message_id\": \"$messageId\",\"channel_id\":\"$channelId\"}"
438 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
439 | logger.debug(res)
440 | return Gson().fromJson(res, Announces::class.java)
441 | }
442 |
443 | /**
444 | * 删除全频道公告
445 | * @param guildId 频道id
446 | * @param messageId 消息id
447 | */
448 | @JvmStatic
449 | fun deleteGuildAnnounces(guildId: String, messageId: String): Boolean {
450 | val url = deleteGuildAnnounce.replace("{{guild_id}}", guildId).replace("{{message_id}}", messageId)
451 | val res = OkHttpUtils.delete(url, mutableMapOf(), officeApiHeader())
452 | logger.debug(res.code.toString())
453 | return res.code == 204
454 | }
455 |
456 |
457 | /**
458 | * 创建子频道公告
459 | * @param channelId 子频道id
460 | * @param messageId 消息id
461 | */
462 | @JvmStatic
463 | fun createChannelAnnounces(channelId: String, messageId: String): Announces {
464 | val url = channelAnnounces.replace("{{channel_id}}", channelId)
465 | val json = "{\"message_id\": \"$messageId\"}"
466 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
467 | logger.debug(res)
468 | return Gson().fromJson(res, Announces::class.java)
469 | }
470 |
471 | /**
472 | * 删除子频道公告
473 | * @param channelId 子频道id
474 | * @param messageId 消息id
475 | */
476 | @JvmStatic
477 | fun deleteChannelAnnounces(channelId: String, messageId: String): Boolean {
478 | val url = deleteChannelAnnounces.replace("{{channel_id}}", channelId).replace("{{message_id}}", messageId)
479 | val res = OkHttpUtils.delete(url, mutableMapOf(), officeApiHeader())
480 | logger.debug(res.code.toString())
481 | return res.code == 204
482 | }
483 |
484 | /**
485 | * 则返回结束时间在 since 之后的日程列表
486 | * @param channelId 子频道id
487 | * @param since 时间戳
488 | */
489 | @JvmStatic
490 | fun getScheduleListByTime(channelId: String, since: Long): List {
491 | val url = schedules.replace("{{channel_id}}", channelId)
492 | url.plus("?since=$since")
493 | val res = OkHttpUtils.getJson(url, Headers.headersOf("Authorization", botToken!!))
494 | logger.debug(res)
495 | if (res.contentEquals("null")) {
496 | return mutableListOf()
497 | }
498 | return Gson().fromJson(res, object : TypeToken>() {}.type)
499 | }
500 |
501 | /**
502 | * 返回当天的日程列表
503 | * @param channelId 子频道id
504 | */
505 | @JvmStatic
506 | fun getScheduleList(channelId: String): List {
507 | val url = schedules.replace("{{channel_id}}", channelId)
508 | val res = OkHttpUtils.getJson(url, Headers.headersOf("Authorization", botToken!!))
509 | logger.debug(res)
510 | if (res.contentEquals("null")) {
511 | return mutableListOf()
512 | }
513 | return Gson().fromJson(res, object : TypeToken>() {}.type)
514 | }
515 |
516 | /**
517 | * 获取单个日程信息
518 | * @param channelId 子频道id
519 | * @param scheduleId 日程id
520 | */
521 | @JvmStatic
522 | fun getScheduleById(channelId: String, scheduleId: String): Schedule {
523 | val url = scheduleUrl.replace("{{channel_id}}", channelId).replace("{{schedule_id}}", scheduleId)
524 | val res = OkHttpUtils.getJson(url, Headers.headersOf("Authorization", botToken!!))
525 | logger.debug(res)
526 | return Gson().fromJson(res, Schedule::class.java)
527 | }
528 |
529 |
530 | /**
531 | * 创建日程
532 | * 要求操作人具有管理频道的权限,如果是机器人,则需要将机器人设置为管理员。
533 | * 时间戳的值不允许创建开始时间早于现在的;
534 | * 结束时间戳不能超过创建开始的时间戳7天
535 | * @param channelId 子频道id
536 | * @param schedule 日程对象
537 | */
538 | @JvmStatic
539 | fun createSchedule(channelId: String, schedule: Schedule): Schedule {
540 | val url = schedules.replace("{{channel_id}}", channelId)
541 | schedule.creator = null
542 | val json = "{\"schedule\": ${Gson().toJson(schedule)}}"
543 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
544 | logger.debug(res)
545 | return Gson().fromJson(res, Schedule::class.java)
546 | }
547 |
548 | /**
549 | * 修改日程
550 | * 要求操作人具有管理频道的权限,如果是机器人,则需要将机器人设置为管理员。
551 | * @param channelId 子频道id
552 | * @param scheduleId 日程id
553 | * @param schedule 日程对象,不需要带id
554 | */
555 | @JvmStatic
556 | fun changeScheduleById(channelId: String, scheduleId: String, schedule: Schedule): Schedule {
557 | val url = scheduleUrl.replace("{{channel_id}}", channelId).replace("{{schedule_id}}", scheduleId)
558 | schedule.creator = null
559 | val json = "{\"schedule\": ${Gson().toJson(schedule)}}"
560 | val res = OkHttpUtils.patchJson(url, OkHttpUtils.addJson(json), officeApiHeader())
561 | logger.debug(res)
562 | return Gson().fromJson(res, Schedule::class.java)
563 | }
564 |
565 | /**
566 | * 删除日程
567 | * 要求操作人具有管理频道的权限,如果是机器人,则需要将机器人设置为管理员。
568 | * @param channelId 子频道id
569 | * @param scheduleId 日程id
570 | */
571 | @JvmStatic
572 | fun deleteScheduleById(channelId: String, scheduleId: String): Boolean {
573 | val url = scheduleUrl.replace("{{channel_id}}", channelId).replace("{{schedule_id}}", scheduleId)
574 | val res = OkHttpUtils.delete(url, mutableMapOf(), officeApiHeader())
575 | logger.debug(res.code.toString())
576 | return res.code == 204
577 | }
578 |
579 |
580 | /**
581 | * 撤回当前消息
582 | * 用来撤回频道内的消息
583 | * 管理员可以撤回普通成员的消息
584 | * 频道主可以撤回所有人的消息
585 | */
586 | @JvmStatic
587 | fun recall(channelId:String,messageId: String): Boolean {
588 | val url = recall.replace("{{channel_id}}", channelId).replace("{{message_id}}", messageId)
589 | val res = OkHttpUtils.delete(url, mutableMapOf(), officeApiHeader())
590 | logger.debug(res.code.toString())
591 | return res.code == 200
592 | }
593 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/common/BotClient.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.common
2 |
3 | import com.hcyacg.protocol.constant.Constant.Companion.accessWithFragmentedWss
4 | import com.hcyacg.protocol.internal.config.IdentifyConfig
5 |
6 | open class BotClient(
7 | config: IdentifyConfig,
8 | officialEvents: List = emptyList(),
9 | uri: String = accessWithFragmentedWss!!.url,
10 | ) : SequelBotClient(
11 | uri = uri,
12 | BotListener(config, officialEvents)
13 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/common/BotEvent.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.common
2 |
3 | import com.hcyacg.protocol.constant.Constant.Companion.botToken
4 | import com.hcyacg.protocol.event.*
5 | import com.hcyacg.protocol.internal.config.IdentifyConfig
6 | import com.hcyacg.protocol.event.ReadyEvent
7 | import com.hcyacg.protocol.event.message.*
8 | import com.hcyacg.protocol.event.message.directMessage.DirectMessageCreateEvent
9 |
10 | /**
11 | * 频道事件
12 | */
13 | abstract class BotEvent {
14 |
15 |
16 | open fun getToken(): String? {
17 | return botToken
18 | }
19 |
20 | open suspend fun onReady(event: ReadyEvent) {}
21 |
22 | open suspend fun onGuildMemberAdd(event: GuildMemberEvent) {}
23 |
24 | open suspend fun onGuildMemberUpdate(event: GuildMemberEvent) {}
25 |
26 | open suspend fun onGuildMemberRemove(event: GuildMemberEvent) {}
27 |
28 | open suspend fun onAtMessageCreate(event: AtMessageCreateEvent) {}
29 |
30 | open suspend fun onMessageCreate(event: MessageCreateEvent) {}
31 |
32 | open suspend fun onDirectMessageCreate(event: DirectMessageCreateEvent) {}
33 |
34 | open suspend fun onChannelCreate(event: ChannelEvent) {}
35 |
36 | open suspend fun onChannelUpdate(event: ChannelEvent) {}
37 |
38 | open suspend fun onChannelDelete(event: ChannelEvent) {}
39 |
40 | open suspend fun onGuildCreate(event: GuildEvent) {}
41 |
42 | open suspend fun onGuildUpdate(event: GuildEvent) {}
43 |
44 | open suspend fun onGuildDelete(event: GuildEvent) {}
45 |
46 | open suspend fun onResumed(config: IdentifyConfig, sessionId: String) {}
47 |
48 | open suspend fun onMessageReactionAdd(event: MessageReactionEvent) {}
49 |
50 | open suspend fun onMessageReactionRemove(event: MessageReactionEvent) {}
51 |
52 | open suspend fun onMessageAuditPass(event: MessageAuditPassEvent) {}
53 |
54 | open suspend fun onMessageAuditReject(event: MessageAuditRejectEvent) {}
55 |
56 | open suspend fun onAudioStart(event: AudioActionEvent) {}
57 |
58 | open suspend fun onAudioFinish(event: AudioActionEvent) {}
59 |
60 | open suspend fun onAudioOnMic(event: AudioActionEvent) {}
61 |
62 | open suspend fun onAudioOffMic(event: AudioActionEvent) {}
63 |
64 | //TODO 官方有许多事件,后续在这里添加事件名称
65 |
66 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/common/BotListener.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.common
2 |
3 |
4 | import com.google.gson.Gson
5 | import com.google.gson.reflect.TypeToken
6 | import com.hcyacg.protocol.constant.Constant.Companion.logger
7 | import com.hcyacg.protocol.constant.Constant.Companion.threadLocal
8 | import com.hcyacg.protocol.event.*
9 | import com.hcyacg.protocol.internal.BaseBotListener
10 | import com.hcyacg.protocol.internal.config.IdentifyConfig
11 | import com.hcyacg.protocol.internal.entity.*
12 | import com.hcyacg.protocol.internal.enums.DispatchEnums
13 | import com.hcyacg.protocol.internal.enums.OPCodeEnums.*
14 | import com.hcyacg.protocol.utils.ScheduleUtils
15 | import okhttp3.Response
16 | import okhttp3.WebSocket
17 | import com.hcyacg.protocol.event.ReadyEvent
18 | import com.hcyacg.protocol.event.message.*
19 | import com.hcyacg.protocol.event.message.directMessage.DirectMessageCreateEvent
20 | import com.hcyacg.protocol.internal.enums.OPCodeEnums
21 | import kotlinx.coroutines.*
22 | import java.util.*
23 | import java.util.concurrent.atomic.AtomicBoolean
24 | import java.util.concurrent.atomic.AtomicLong
25 |
26 | class BotListener(
27 | private val config: IdentifyConfig,
28 | private val officialEvents: List = emptyList(),
29 | private val heartbeatDelay: Long = 30000,
30 | private val reconnectTimeout: Long = 60000,
31 | ) : BaseBotListener() {
32 | private val identifyOpDto = Gson().toJson(Identify(config.toIdentifyOperationData()))
33 | var sessionId: String = ""
34 |
35 | private val logHeader = "${config.index + 1} of ${config.shards}"
36 |
37 | private var hbTimer: Timer? = null
38 | private val messageSeq by lazy { AtomicLong(0) }
39 | private val lastReceivedHeartBeat = AtomicLong(0)
40 | private val isResume = AtomicBoolean(false)
41 |
42 |
43 | override fun onOpen(webSocket: WebSocket, response: Response) {
44 | logger.info("$logHeader 正在连接中 ")
45 | }
46 |
47 | inline fun Gson.fromJson(json: String) = this.fromJson(json, object : TypeToken() {}.type)
48 |
49 |
50 | @OptIn(DelicateCoroutinesApi::class)
51 | override fun onMessage(webSocket: WebSocket, text: String) {
52 | runCatching {
53 | // logger.trace("$logHeader 收到了信息 $text")
54 | Gson().fromJson(text, Operation::class.java)?.also { opType ->
55 |
56 | when (OPCodeEnums.getOPCodeByCode(opType.op)) {
57 | HEARTBEAT_ACK -> {
58 | lastReceivedHeartBeat.getAndSet(System.currentTimeMillis())
59 | }
60 | //首次连接 发送Identify信息鉴权
61 | HELLO -> {
62 | // 初始化操作
63 | initConnection(webSocket)
64 | }
65 | //收到事件
66 | DISPATCH -> {
67 | Gson().fromJson(text, DispatchType::class.java)?.also { dispatchDto ->
68 | successConnect(dispatchDto)
69 | // logger.debug("$logHeader 收到了事件:${dispatchDto.type} 内容:$text")
70 | when (dispatchDto.type) {
71 |
72 | DispatchEnums.READY -> {
73 |
74 | Gson().fromJson>(text)?.also { readyEvent ->
75 | sessionId = readyEvent.d.sessionId
76 | officialEvents.forEach {
77 | runBlocking {
78 | it.onReady(readyEvent.d)
79 | }
80 | }
81 |
82 | }
83 |
84 | }
85 | DispatchEnums.GUILD_MEMBER_ADD -> {
86 | Gson().fromJson>(text)?.also { guildMemberEvent ->
87 | officialEvents.forEach {
88 | runBlocking {
89 | it.onGuildMemberAdd(guildMemberEvent.d)
90 | }
91 | }
92 |
93 | }
94 | }
95 | DispatchEnums.GUILD_MEMBER_UPDATE -> {
96 | Gson().fromJson>(text)?.also { guildMemberEvent ->
97 | officialEvents.forEach {
98 | runBlocking {
99 | it.onGuildMemberUpdate(guildMemberEvent.d)
100 | }
101 | }
102 | }
103 | }
104 | DispatchEnums.GUILD_MEMBER_REMOVE -> {
105 | Gson().fromJson>(text)?.also { guildMemberEvent ->
106 | officialEvents.forEach {
107 | runBlocking {
108 | it.onGuildMemberRemove(guildMemberEvent.d)
109 | }
110 | }
111 | }
112 | }
113 | DispatchEnums.AT_MESSAGE_CREATE -> {
114 | Gson().fromJson>(text)?.also { guildAtMessage ->
115 | officialEvents.forEach {
116 | GlobalScope.launch(threadLocal.asContextElement(guildAtMessage.d.guildId)) {
117 | it.onAtMessageCreate(guildAtMessage.d)
118 | }
119 | }
120 | }
121 | }
122 | DispatchEnums.MESSAGE_CREATE -> {
123 | Gson().fromJson>(text)?.also { guildAtMessage ->
124 | officialEvents.forEach {
125 | GlobalScope.launch(threadLocal.asContextElement(guildAtMessage.d.guildId)) {
126 | it.onMessageCreate(guildAtMessage.d)
127 | }
128 | }
129 | }
130 | }
131 | DispatchEnums.CHANNEL_CREATE -> {
132 | Gson().fromJson>(text)?.also { channelEvent ->
133 | officialEvents.forEach {
134 | runBlocking {
135 | it.onChannelCreate(channelEvent.d)
136 | }
137 | }
138 | }
139 | }
140 | DispatchEnums.CHANNEL_UPDATE -> {
141 | Gson().fromJson>(text)?.also { channelEvent ->
142 | officialEvents.forEach {
143 | runBlocking {
144 | it.onChannelUpdate(channelEvent.d)
145 | }
146 | }
147 | }
148 | }
149 | DispatchEnums.CHANNEL_DELETE -> {
150 | Gson().fromJson>(text)?.also { channelEvent ->
151 | officialEvents.forEach {
152 | runBlocking {
153 | it.onChannelDelete(channelEvent.d)
154 | }
155 | }
156 | }
157 | }
158 | DispatchEnums.GUILD_CREATE -> {
159 | Gson().fromJson>(text)?.also { guildEvent ->
160 | officialEvents.forEach {
161 | runBlocking {
162 | it.onGuildCreate(guildEvent.d)
163 | }
164 | }
165 | }
166 | }
167 | DispatchEnums.GUILD_UPDATE -> {
168 | Gson().fromJson>(text)?.also { guildEvent ->
169 | officialEvents.forEach {
170 | runBlocking {
171 | it.onGuildUpdate(guildEvent.d)
172 | }
173 | }
174 |
175 | }
176 | }
177 | DispatchEnums.GUILD_DELETE -> {
178 | Gson().fromJson>(text)?.also { guildEvent ->
179 | officialEvents.forEach {
180 | runBlocking {
181 | it.onGuildDelete(guildEvent.d)
182 | }
183 | }
184 | }
185 | }
186 | DispatchEnums.RESUMED -> {
187 | officialEvents.forEach { runBlocking { it.onResumed(config, sessionId) } }
188 | }
189 | DispatchEnums.MESSAGE_REACTION_ADD -> {
190 | Gson().fromJson>(text)
191 | ?.also { messageReactionEvent ->
192 | officialEvents.forEach {
193 | runBlocking {
194 | it.onMessageReactionAdd(messageReactionEvent.d)
195 | }
196 | }
197 |
198 |
199 | }
200 | }
201 | DispatchEnums.MESSAGE_REACTION_REMOVE -> {
202 | Gson().fromJson>(text)
203 | ?.also { messageReactionEvent ->
204 | officialEvents.forEach {
205 | runBlocking {
206 | it.onMessageReactionRemove(messageReactionEvent.d)
207 | }
208 | }
209 |
210 | }
211 | }
212 | DispatchEnums.DIRECT_MESSAGE_CREATE -> {
213 | Gson().fromJson>(text)
214 | ?.also { directMessageCreateEvent ->
215 | officialEvents.forEach {
216 | runBlocking {
217 | it.onDirectMessageCreate(directMessageCreateEvent.d)
218 | }
219 | }
220 |
221 | }
222 | logger.debug("$logHeader 收到了事件:${dispatchDto.type} 内容:$text")
223 | }
224 | DispatchEnums.THREAD_CREATE -> {
225 | logger.debug("$logHeader 收到了事件:${dispatchDto.type} 内容:$text")
226 | }
227 | DispatchEnums.THREAD_UPDATE -> {
228 | logger.debug("$logHeader 收到了事件:${dispatchDto.type} 内容:$text")
229 | }
230 | DispatchEnums.THREAD_DELETE -> {
231 | logger.debug("$logHeader 收到了事件:${dispatchDto.type} 内容:$text")
232 | }
233 | DispatchEnums.POST_CREATE -> {
234 | logger.debug("$logHeader 收到了事件:${dispatchDto.type} 内容:$text")
235 | }
236 | DispatchEnums.POST_DELETE -> {
237 | logger.debug("$logHeader 收到了事件:${dispatchDto.type} 内容:$text")
238 | }
239 | DispatchEnums.REPLY_CREATE -> {
240 | logger.debug("$logHeader 收到了事件:${dispatchDto.type} 内容:$text")
241 | }
242 | DispatchEnums.REPLY_DELETE -> {
243 | logger.debug("$logHeader 收到了事件:${dispatchDto.type} 内容:$text")
244 | }
245 | DispatchEnums.AUDIO_START -> {
246 | Gson().fromJson>(text)?.also { audioActionEvent ->
247 | officialEvents.forEach {
248 | runBlocking {
249 | it.onAudioStart(audioActionEvent.d)
250 | }
251 | }
252 | }
253 | }
254 | DispatchEnums.AUDIO_FINISH -> {
255 | Gson().fromJson>(text)?.also { audioActionEvent ->
256 | officialEvents.forEach {
257 | runBlocking {
258 | it.onAudioFinish(audioActionEvent.d)
259 | }
260 | }
261 | }
262 | }
263 | DispatchEnums.AUDIO_ON_MIC -> {
264 | Gson().fromJson>(text)?.also { audioActionEvent ->
265 | officialEvents.forEach {
266 | runBlocking {
267 | it.onAudioOnMic(audioActionEvent.d)
268 | }
269 | }
270 | }
271 | }
272 | DispatchEnums.AUDIO_OFF_MIC -> {
273 | Gson().fromJson>(text)?.also { audioActionEvent ->
274 | officialEvents.forEach {
275 | runBlocking {
276 | it.onAudioOffMic(audioActionEvent.d)
277 | }
278 | }
279 | }
280 | }
281 | DispatchEnums.MESSAGE_AUDIT_PASS -> {
282 | Gson().fromJson>(text)?.also { messageAuditPassEvent ->
283 | officialEvents.forEach {
284 | runBlocking {
285 | it.onMessageAuditPass(messageAuditPassEvent.d)
286 | }
287 | }
288 | }
289 | }
290 | DispatchEnums.MESSAGE_AUDIT_REJECT -> {
291 | Gson().fromJson>(text)?.also { messageAuditRejectEvent ->
292 | officialEvents.forEach {
293 | runBlocking {
294 | it.onMessageAuditReject(messageAuditRejectEvent.d)
295 | }
296 | }
297 | }
298 | }
299 | else -> {
300 | logger.warn("$logHeader 未知的事件! 信息:$text")
301 | }
302 | }
303 | }
304 | }
305 | RECONNECT -> {
306 | logger.warn("$logHeader 需要重连!")
307 | isResume.getAndSet(true)
308 | reconnectClient()
309 | }
310 | INVALID_SESSION -> {
311 | logger.error("$logHeader 错误:", INVALID_SESSION.description)
312 | failureConnect(webSocket)
313 |
314 | }
315 | HEARTBEAT -> TODO()
316 | IDENTIFY -> TODO()
317 | RESUME -> TODO()
318 | UNKNOWN -> TODO()
319 | }
320 |
321 | }
322 | }.onFailure {
323 | it.printStackTrace()
324 | }
325 | }
326 |
327 | private fun failureConnect(webSocket: WebSocket) {
328 | if (isResume.get()) {
329 | isResume.set(false)
330 | reconnectClient()
331 | } else {
332 | webSocket.cancel()
333 | hbTimer?.cancel()
334 | throw RuntimeException(INVALID_SESSION.description)
335 | }
336 | }
337 |
338 | private fun successConnect(dispatchDto: DispatchType) {
339 | messageSeq.getAndSet(dispatchDto.seq)
340 | isResume.set(false)
341 | }
342 |
343 | override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
344 | logger.warn("$logHeader 正在关闭")
345 | }
346 |
347 | override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
348 | logger.warn("$logHeader 已关闭")
349 | }
350 |
351 | override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
352 | logger.warn("$logHeader 尝试重新连接")
353 | reconnectClient()
354 | }
355 |
356 | private fun initConnection(webSocket: WebSocket) {
357 | // 鉴权
358 | if (isResume.get()) {
359 | val resume = Resume(ResumeData(messageSeq.get(), sessionId, config.token))
360 | webSocket.sendAndPrintLog(Gson().toJson(resume))
361 | } else {
362 | webSocket.sendAndPrintLog(identifyOpDto)
363 | }
364 | // 启动心跳发送
365 | lastReceivedHeartBeat.getAndSet(System.currentTimeMillis())
366 | val processor = createHeartBeatProcessor(webSocket)
367 | // 先取消以前的定时器
368 | hbTimer?.cancel()
369 | // 启动新的心跳
370 | hbTimer = ScheduleUtils.loopEvent(processor, Date(), heartbeatDelay)
371 | }
372 |
373 | private fun createHeartBeatProcessor(webSocket: WebSocket): suspend () -> Unit {
374 | return suspend {
375 | val last = lastReceivedHeartBeat.get()
376 | val now = System.currentTimeMillis()
377 | if (now - last > reconnectTimeout) {
378 | logger.warn("$logHeader 心跳超时,尝试重新连接")
379 | reconnectClient()
380 | } else {
381 | val hb = Gson().toJson(Heartbeat(messageSeq.get()))
382 | webSocket.sendAndPrintLog(hb, true)
383 | }
384 | }
385 | }
386 |
387 | private fun reconnectClient() {
388 | hbTimer?.cancel()
389 | reconnect()
390 | }
391 |
392 | private fun WebSocket.sendAndPrintLog(text: String, isHeartbeat: Boolean = false) {
393 | // if (isHeartbeat) {
394 | // logger.debug("$logHeader 发送心跳包 $text")
395 | // } else {
396 | // logger.info("$logHeader 发送信息 $text")
397 | // }
398 | this.send(text)
399 | }
400 |
401 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/common/BotManager.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.common
2 |
3 | import com.hcyacg.protocol.constant.Constant.Companion.botToken
4 | import com.hcyacg.protocol.internal.config.IdentifyConfig
5 | import com.hcyacg.protocol.internal.config.Intents
6 | import kotlinx.coroutines.runBlocking
7 |
8 | object BotManager {
9 |
10 | private var isPrivate: Boolean = false
11 | private lateinit var token: String
12 |
13 |
14 | /**
15 | * 默认公域
16 | */
17 | private var intent: Intents = Intents(
18 | guilds = true,
19 | guildMembers = true,
20 | directMessage = false,
21 | audioAction = true,
22 | atMessages = true,
23 | messages = false,
24 | forum = false,
25 | guildMessageReactions = false,
26 | messageAudit = true
27 | )
28 |
29 | @JvmStatic
30 | fun configuration(token:String, isPrivate:Boolean):BotManager{
31 | this.token = token
32 | this.isPrivate = isPrivate
33 |
34 | botToken = token
35 |
36 | /**
37 | * 判断是否是私域
38 | */
39 | if (BotManager.isPrivate) {
40 | intent = Intents(
41 | guilds = true,
42 | guildMembers = true,
43 | directMessage = true,
44 | audioAction = true,
45 | atMessages = true,
46 | messages = true,
47 | forum = false,
48 | guildMessageReactions = true,
49 | messageAudit = true
50 | )
51 | }
52 | return this
53 | }
54 |
55 |
56 | @JvmStatic
57 | fun addListen(vararg listener: BotEvent) {
58 | val list = listener.toMutableList()
59 | list.add(MonitorMessage())
60 | val gatewayAccessWithFragmentedWss = Gateway.gatewayAccessWithFragmentedWss(botToken!!)
61 |
62 | for (i in 0 until gatewayAccessWithFragmentedWss!!.shards) {
63 | runBlocking {
64 | BotClient(IdentifyConfig(botToken!!, gatewayAccessWithFragmentedWss.shards, i, intent), list)
65 | }
66 | }
67 |
68 | }
69 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/common/ExcepetionHandler.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.common
2 |
3 | import com.google.gson.Gson
4 | import com.google.gson.JsonParser
5 | import okhttp3.Response
6 |
7 | internal class ExceptionHandler(response: Response) {
8 |
9 | init {
10 | val regex = Regex(pattern = "20[0-9]")
11 | if (regex.containsMatchIn(response.code.toString())) {
12 | var traceId = response.headers["X-Tps-trace-ID"]
13 | val message = exceptionCode(response.code)
14 | }else {
15 | val result = response.body?.string()
16 | val code = JsonParser.parseString(result).asJsonObject.get("code").asInt
17 | val message = exceptionCode(code)
18 | }
19 | }
20 |
21 |
22 | private fun exceptionCode(code: Int): String? {
23 | when (code) {
24 | 401 -> return "认证失败"
25 | 404 -> return "未找到 API"
26 | 405 -> return "http method 不允许"
27 | 429 -> return "频率限制"
28 | 500 -> return "处理失败"
29 | 504 -> return "处理失败"
30 | 10001 -> return "UnknownAccount 账号异常"
31 | 10003 -> return "UnknownChannel 子频道异常"
32 | 10004 -> return "UnknownGuild 频道异常"
33 | 11281 -> return "ErrorCheckAdminFailed 检查是否是管理员失败,系统错误,一般重试一次会好,最多只能重试一次"
34 | 11282 -> return "ErrorCheckAdminNotPass 检查是否是管理员未通过,该接口需要管理员权限,但是用户在添加机器人的时候并未授予该权限,属于逻辑错误,可以提示用户进行授权"
35 | 11251 -> return "ErrorWrongAppid 参数中的 appid 错误,开发者填的 token 错误,appid 无法识别"
36 | 11252 -> return "ErrorCheckAppPrivilegeFailed 检查应用权限失败,系统错误,一般重试一次会好,最多只能重试一次"
37 | 11253 -> return "ErrorCheckAppPrivilegeNotPass 检查应用权限不通过,该机器人应用未获得调用该接口的权限,需要向平台申请"
38 | 11254 -> return "ErrorInterfaceForbidden 应用接口被封禁,该机器人虽然获得了该接口权限,但是被封禁了。"
39 | 11261 -> return "ErrorWrongAppid 参数中缺少 appid,同 11251"
40 | 11262 -> return "ErrorCheckRobot 当前接口不支持使用机器人 Bot Token 调用"
41 | 11263 -> return "ErrorCheckGuildAuth 检查频道权限失败,系统错误,一般重试一次会好,最多只能重试一次"
42 | 11264 -> return "ErrorGuildAuthNotPass 检查小站权限未通过,管理员添加机器人的时候未授予该接口权限,属于逻辑错误,可提示用户进行授权"
43 | 11265 -> return "ErrorRobotHasBaned 机器人已经被封禁"
44 | 11241 -> return "ErrorWrongToken 参数中缺少 token"
45 | 11242 -> return "ErrorCheckTokenFailed 校验 token 失败,系统错误,一般重试一次会好,最多只能重试一次"
46 | 11243 -> return "ErrorCheckTokenNotPass 校验 token 未通过,用户填充的 token 错误,需要开发者进行检查"
47 | 11273 -> return "ErrorCheckUserAuth 检查用户权限失败,当前接口不支持使用 Bearer Token 调用"
48 | 11274 -> return "ErrorUserAuthNotPass 检查用户权限未通过,用户 OAuth 授权时未给与该接口权限,可提示用户重新进行授权"
49 | 11275 -> return "ErrorWrongAppid 无 appid ,同 11251"
50 | 12001 -> return "ReplaceIDFailed 替换 id 失败"
51 | 12002 -> return "RequestInvalid 请求体错误"
52 | 12003 -> return "ResponseInvalid 回包错误"
53 | 20028 -> return "ChannelHitWriteRateLimit 子频道消息触发限频"
54 | 50006 -> return "CannotSendEmptyMessage 消息为空"
55 | 50035 -> return "InvalidFormBody form-data 内容异常"
56 | 301000 -> return "参数错误"
57 | 301001 -> return "查询频道信息错误"
58 | 301002 -> return "查询子频道权限错误"
59 | 301003 -> return "修改子频道权限错误"
60 | 301004 -> return "私密子频道关联的人数到达上限"
61 | 301005 -> return "调用Rpc服务失败"
62 | 301006 -> return "非群成员没有查询权限"
63 | 301007 -> return "参数超过数量限制"
64 |
65 | 502001 -> return "频道id无效"
66 | 502002 -> return "频道id为空"
67 | 502003 -> return "用户id无效"
68 | 502004 -> return "用户id为空"
69 | 502005 -> return "timestamp不合法"
70 | 502006 -> return "timestamp无效"
71 | 502007 -> return "参数转换错误"
72 | 502008 -> return "rpc调用失败"
73 | 502009 -> return "安全打击"
74 | 502010 -> return "请求头错误"
75 | 304003 -> return "URL_NOT_ALLOWED url 未报备"
76 | 304004 -> return "ARK_NOT_ALLOWED 没有发 ark 消息权限"
77 | 304005 -> return "EMBED_LIMIT embed 长度超限"
78 | 304006 -> return "SERVER_CONFIG 后台配置错误"
79 | 304007 -> return "GET_GUILD 查询频道异常"
80 | 304008 -> return "GET_BOT 查询机器人异常"
81 | 304009 -> return "GET_CHENNAL 查询子频道异常"
82 | 304010 -> return "CHANGE_IMAGE_URL 图片转存错误"
83 | 304011 -> return "NO_TEMPLATE 模板不存在"
84 | 304012 -> return "GET_TEMPLATE 取模板错误"
85 | 304014 -> return "ARK_PRIVILEGE 没有模板权限"
86 | 304016 -> return "SEND_ERROR 发消息错误"
87 | 304017 -> return "UPLOAD_IMAGE 图片上传错误"
88 | 304018 -> return "SESSION_NOT_EXIST 机器人没连上 gateway"
89 | 304019 -> return "AT_EVERYONE_TIMES @全体成员 次数超限"
90 | 304020 -> return "FILE_SIZE 文件大小超限"
91 | 304021 -> return "GET_FILE 下载文件错误"
92 | 304022 -> return "PUSH_TIME 推送消息时间限制"
93 | 304023 -> return "PUSH_MSG_ASYNC_OK 推送消息异步调用成功, 等待人工审核"
94 | 304024 -> return "REPLY_MSG_ASYNC_OK 回复消息异步调用成功, 等待人工审核"
95 | 304025 -> return "BEAT 消息被打击"
96 | 304026 -> return "MSG_ID 回复的消息 id 错误"
97 | 304027 -> return "MSG_EXPIRE 回复的消息过期"
98 | 304028 -> return "MSG_PROTECT 非 At 当前用户的消息不允许回复"
99 | 304029 -> return "CORPUS_ERROR 调语料服务错误"
100 | 304030 -> return "CORPUS_NOT_MATCH 语料不匹配"
101 | 306001 -> return "param invalid 撤回消息参数错误"
102 | 306002 -> return "msgid error 消息id错误"
103 | 306003 -> return "fail to get message 获取消息错误(可重试)"
104 | 306004 -> return "no permission to delete message 没有撤回此消息的权限"
105 | 306005 -> return "retract message error 消息撤回失败(可重试)"
106 | 306006 -> return "fail to get channel 获取子频道失败(可重试)"
107 |
108 | 501001 -> return "参数校验失败"
109 | 501002 -> return "创建子频道公告失败(可重试)"
110 | 501003 -> return "删除子频道公告失败(可重试)"
111 | 501004 -> return "获取频道信息失败(可重试)"
112 | 501005 -> return "MessageID 错误"
113 | 501006 -> return "创建频道全局公告失败(可重试)"
114 | 501007 -> return "删除频道全局公告失败(可重试)"
115 | 501008 -> return "MessageID 不存在"
116 | 1100100 -> return "安全打击:消息被限频"
117 | 1100101 -> return "安全打击:内容涉及敏感,请返回修改"
118 | 1100102 -> return "安全打击:抱歉,暂未获得新功能体验资格"
119 | 1100103 -> return "安全打击"
120 | 1100104 -> return "安全打击:该群已失效或当前群已不存在"
121 | 1100300 -> return "系统内部错误"
122 | 1100301 -> return "调用方不是群成员"
123 | 1100302 -> return "获取指定频道名称失败"
124 | 1100303 -> return "主页频道非管理员不允许发消息"
125 | 1100304 -> return "@次数鉴权失败"
126 | 1100305 -> return "TinyId 转换 Uin 失败"
127 | 1100306 -> return "非私有频道成员"
128 | 1100307 -> return "非白名单应用子频道"
129 | 1100308 -> return "触发频道内限频"
130 | 1100499 -> return "其他错误"
131 | }
132 | return null
133 | }
134 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/common/Gateway.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.common
2 |
3 |
4 | import com.google.gson.Gson
5 | import com.hcyacg.protocol.constant.Constant
6 | import com.hcyacg.protocol.constant.Constant.Companion.accessWithFragmentedWss
7 | import com.hcyacg.protocol.constant.Constant.Companion.logger
8 | import com.hcyacg.protocol.internal.entity.AccessWithFragmentedWss
9 | import okhttp3.Headers
10 | import okhttp3.OkHttpClient
11 | import okhttp3.Request
12 | import okhttp3.RequestBody
13 | import java.util.concurrent.TimeUnit
14 |
15 | class Gateway {
16 |
17 | companion object {
18 | private val headers = Headers.Builder()
19 | private var requestBody: RequestBody? = null
20 |
21 | private val client: OkHttpClient by lazy {
22 | OkHttpClient.Builder()
23 | .readTimeout(30, TimeUnit.SECONDS)
24 | .writeTimeout(30, TimeUnit.SECONDS)
25 | .connectTimeout(30, TimeUnit.SECONDS)
26 | .build()
27 | }
28 |
29 | /**
30 | * 获取wss的地址以及相关数据 封装成对象
31 | */
32 | fun gatewayAccessWithFragmentedWss(token: String): AccessWithFragmentedWss? {
33 | if (null != accessWithFragmentedWss) {
34 | return accessWithFragmentedWss
35 | }
36 | headers.add("Authorization", token)
37 | val request: Request by lazy {
38 | Request.Builder().get().url(Constant.proUrl + "/gateway/bot").header("Authorization", token).build()
39 | }
40 | val execute = client.newCall(request).execute()
41 | val string = execute.body!!.string()
42 | logger.debug(string)
43 | accessWithFragmentedWss = Gson().fromJson(string, AccessWithFragmentedWss::class.java)
44 | logger.info("wss地址已获取 ${accessWithFragmentedWss!!.url}")
45 | execute.close()
46 | return accessWithFragmentedWss
47 | }
48 |
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/common/MonitorMessage.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.common
2 |
3 | import com.google.gson.Gson
4 | import com.hcyacg.protocol.constant.Constant
5 | import com.hcyacg.protocol.constant.Constant.Companion.bot
6 | import com.hcyacg.protocol.constant.Constant.Companion.logger
7 | import com.hcyacg.protocol.event.*
8 | import com.hcyacg.protocol.event.message.*
9 | import com.hcyacg.protocol.event.message.directMessage.DirectMessageCreateEvent
10 |
11 |
12 | internal class MonitorMessage : BotEvent() {
13 |
14 | override suspend fun onReady(event: ReadyEvent) {
15 | bot = event.user
16 | logger.info("${event.shard[0] + 1} of ${event.shard[1]} 已连接")
17 | }
18 |
19 | override suspend fun onGuildMemberAdd(event: GuildMemberEvent) {
20 | logger.info("${event.user.username}(${event.user.id}) 加入了 ${BotApi.getGuildById(event.guildId).name}(${event.guildId})")
21 | }
22 |
23 | override suspend fun onGuildMemberUpdate(event: GuildMemberEvent) {}
24 |
25 | override suspend fun onGuildMemberRemove(event: GuildMemberEvent) {
26 | logger.info("${event.user.username}(${event.user.id}) 退出了 ${BotApi.getGuildById(event.guildId).name}(${event.guildId})")
27 | }
28 |
29 | override suspend fun onAtMessageCreate(event: AtMessageCreateEvent) {
30 | logger.info(
31 | "${BotApi.getGuildById(event.guildId).name}(${event.guildId}) - ${
32 | BotApi.getChannelInfo(
33 | event.channelId
34 | ).name
35 | }(${event.channelId}) - ${event.author.username}(${event.author.id}): ${event.content}"
36 | )
37 | }
38 |
39 | override suspend fun onMessageCreate(event: MessageCreateEvent) {
40 | if (event.isContentInitialized() && event.content.indexOf("<@!${bot!!.id}>") < 0) {
41 | logger.info(
42 | "${BotApi.getGuildById(event.guildId).name}(${event.guildId}) - ${
43 | BotApi.getChannelInfo(
44 | event.channelId
45 | ).name
46 | }(${event.channelId}) - ${event.author.username}(${event.author.id}):${
47 | if (null != event.attachments && event.attachments.isNotEmpty()) Gson().toJson(
48 | event.attachments
49 | ) else ""
50 | } ${event.content}"
51 | )
52 | }
53 | }
54 |
55 | override suspend fun onDirectMessageCreate(event: DirectMessageCreateEvent) {
56 | if (event.isContentInitialized() && event.content.indexOf("<@!${bot!!.id}>") < 0) {
57 | logger.info(
58 | "${event.author.username}(${event.author.id}): ${event.content}"
59 | )
60 | }
61 | }
62 |
63 | override suspend fun onChannelCreate(event: ChannelEvent) {
64 | logger.info("${BotApi.getGuildById(event.guildId).name}(${event.guildId}) - ${event.name}(${event.id}) 子频道被创建")
65 | }
66 |
67 | override suspend fun onChannelUpdate(event: ChannelEvent) {
68 | logger.info("${BotApi.getGuildById(event.guildId).name}(${event.guildId}) - ${event.name}(${event.id}) 子频道信息已被修改")
69 | }
70 |
71 | override suspend fun onChannelDelete(event: ChannelEvent) {
72 | logger.info("${BotApi.getGuildById(event.guildId).name}(${event.guildId}) - ${event.name}(${event.id}) 子频道被删除")
73 | }
74 |
75 | override suspend fun onGuildCreate(event: GuildEvent) {
76 | logger.info("机器人加入了 ${event.name}(${event.id}) 介绍: ${event.description}")
77 | }
78 |
79 | override suspend fun onGuildUpdate(event: GuildEvent) {
80 | logger.info("${event.name}(${event.id}) 频道信息变更")
81 |
82 | }
83 |
84 | override suspend fun onGuildDelete(event: GuildEvent) {
85 | logger.info("机器人离开了 ${event.name}(${event.id})")
86 | }
87 |
88 | override suspend fun onMessageReactionAdd(event: MessageReactionEvent) {
89 | val memberInfo = BotApi.getMemberInfo(event.guildId, event.userId)
90 | logger.info(
91 | "${BotApi.getGuildById(event.guildId).name}(${event.guildId}) - ${
92 | BotApi.getChannelInfo(
93 | event.channelId
94 | ).name
95 | }(${event.channelId}) - ${memberInfo.user!!.username}(${event.userId}): 添加了表情表态对象: ${event.emoji} ${
96 | getInfo(
97 | event.emoji.id
98 | )
99 | }"
100 | )
101 | }
102 |
103 | override suspend fun onMessageReactionRemove(event: MessageReactionEvent) {
104 | val memberInfo = BotApi.getMemberInfo(event.guildId, event.userId)
105 | logger.info(
106 | "${BotApi.getGuildById(event.guildId).name}(${event.guildId}) - ${
107 | BotApi.getChannelInfo(
108 | event.channelId
109 | ).name
110 | }(${event.channelId}) - ${memberInfo.user!!.username}(${event.userId}): 删除了表情表态对象: ${event.emoji} ${
111 | getInfo(
112 | event.emoji.id
113 | )
114 | }"
115 | )
116 | }
117 |
118 |
119 | override suspend fun onAudioStart(event: AudioActionEvent) {
120 | logger.info(
121 | "${BotApi.getGuildById(event.guildId).name}(${event.guildId}) - ${
122 | BotApi.getChannelInfo(
123 | event.channelId
124 | ).name
125 | }(${event.channelId}) - ${event.text} -${event.audioUrl}"
126 | )
127 | }
128 |
129 | override suspend fun onAudioFinish(event: AudioActionEvent) {
130 | logger.info(
131 | "${BotApi.getGuildById(event.guildId).name}(${event.guildId}) - ${
132 | BotApi.getChannelInfo(
133 | event.channelId
134 | ).name
135 | }(${event.channelId}) - ${event.text} -${event.audioUrl}"
136 | )
137 | }
138 |
139 | override suspend fun onAudioOnMic(event: AudioActionEvent) {
140 | logger.info(
141 | "${BotApi.getGuildById(event.guildId).name}(${event.guildId}) - ${
142 | BotApi.getChannelInfo(
143 | event.channelId
144 | ).name
145 | }(${event.channelId}) - 上麦了"
146 | )
147 |
148 | }
149 |
150 | override suspend fun onAudioOffMic(event: AudioActionEvent) {
151 | logger.info(
152 | "${BotApi.getGuildById(event.guildId).name}(${event.guildId}) - ${
153 | BotApi.getChannelInfo(
154 | event.channelId
155 | ).name
156 | }(${event.channelId}) - 下麦了"
157 | )
158 |
159 | }
160 |
161 | override suspend fun onMessageAuditPass(event: MessageAuditPassEvent) {
162 | logger.info("${event.createTime} 发送的消息未通过审核")
163 | }
164 |
165 | override suspend fun onMessageAuditReject(event: MessageAuditRejectEvent) {
166 | logger.info("${event.createTime} 发送的消息通过审核")
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/common/SequelBotClient.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.common
2 |
3 | import com.hcyacg.protocol.constant.Constant.Companion.logger
4 | import com.hcyacg.protocol.internal.BaseBotClient
5 | import com.hcyacg.protocol.internal.BaseBotListener
6 | import okhttp3.OkHttpClient
7 | import okhttp3.Request
8 | import java.util.concurrent.TimeUnit
9 |
10 | /**
11 | * 中续 机器人客户端
12 | */
13 | open class SequelBotClient(
14 | private val uri: String,
15 | private val listener: T
16 | ) : BaseBotClient {
17 | private val client: OkHttpClient by lazy {
18 | OkHttpClient.Builder()
19 | .readTimeout(30, TimeUnit.SECONDS)
20 | .writeTimeout(30, TimeUnit.SECONDS)
21 | .connectTimeout(30, TimeUnit.SECONDS)
22 | .build()
23 | }
24 | private val request: Request by lazy { Request.Builder().get().url(uri).build() }
25 |
26 | private var connectWebSocket = client.newWebSocket(request, listener)
27 |
28 | init {
29 | listener.reconnect { reconnect() }
30 | }
31 |
32 | override fun reconnect() {
33 | connectWebSocket.close(1000, "reconnect")
34 | logger.info("重连中 ... ")
35 | connectWebSocket = client.newWebSocket(request, listener)
36 | }
37 |
38 | override fun sendMessage(text: String) {
39 | logger.info("发送信息 $text")
40 | connectWebSocket.send(text)
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/constant/Constant.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.constant
2 |
3 | import com.hcyacg.protocol.event.api.User
4 | import com.hcyacg.protocol.internal.entity.AccessWithFragmentedWss
5 | import org.slf4j.Logger
6 | import org.slf4j.LoggerFactory
7 |
8 | class Constant {
9 | companion object {
10 | /**
11 | * 正式环境
12 | */
13 | const val proUrl: String = "https://api.sgroup.qq.com"
14 |
15 | /**
16 | * 沙箱环境
17 | */
18 | const val testUrl: String = "https://sandbox.api.sgroup.qq.com"
19 |
20 | /**
21 | * 网关返回的数据
22 | */
23 | var accessWithFragmentedWss: AccessWithFragmentedWss? = null
24 |
25 | /**
26 | * 机器人token
27 | */
28 | var botToken: String? = null
29 |
30 | val logger: Logger = LoggerFactory.getLogger(this::class.java)
31 |
32 | /**
33 | * 机器人自身信息
34 | */
35 | var bot: User? = null
36 |
37 | val threadLocal = ThreadLocal()
38 |
39 | }
40 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/entity/Announces.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 |
7 | @NoArg
8 | data class Announces(
9 | @SerializedName("guild_id")
10 | val guildId: String,
11 | @SerializedName("channel_id")
12 | val channelId: String,
13 | @SerializedName("message_id")
14 | val messageId: String,
15 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/entity/AudioControl.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 |
7 | @NoArg
8 | data class AudioControl(
9 | @SerializedName("audio_url")
10 | val audioUrl: String,
11 | @SerializedName("text")
12 | val text: String,
13 | @SerializedName("status")
14 | val status: Int
15 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/entity/Channel.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 | @NoArg
7 | data class Channel(
8 | @SerializedName("id")
9 | val id: String = "",
10 | @SerializedName("guild_id")
11 | val guildId: String = "",
12 | @SerializedName("name")
13 | val name: String = "",
14 | @SerializedName("type")
15 | val type: Int = 0,
16 | @SerializedName("sub_type")
17 | val subType: Int = 0,
18 | @SerializedName("position")
19 | val position: Int = 0,
20 | @SerializedName("parent_id")
21 | val parentId: String = "",
22 | @SerializedName("owner_id")
23 | val ownerId: String = ""
24 | )
25 |
26 | /**
27 | * 子频道种类
28 | */
29 |
30 | object ChannelType {
31 |
32 | /**
33 | * 文字子频道
34 | */
35 | val textSubchannel = 0
36 |
37 | /**
38 | * 保留,不可用
39 | */
40 | val unavailable1 = 1
41 |
42 | /**
43 | * 语音子频道
44 | */
45 | val voiceSubchannel = 2
46 |
47 | /**
48 | * 保留,不可用
49 | */
50 | val unavailable3 = 3
51 |
52 | /**
53 | * 子频道分组
54 | */
55 | val subchannelGrouping = 4
56 |
57 | /**
58 | * 直播子频道
59 | */
60 | val liveSubchannel = 10005
61 |
62 | /**
63 | * 应用子频道
64 | */
65 | val applicationSubchannel = 10006
66 |
67 | /**
68 | * 论坛子频道
69 | */
70 | val forumSubchannel = 10007
71 |
72 | }
73 |
74 | /**
75 | * 文字子频道种类
76 | */
77 |
78 | object ChannelSubType {
79 |
80 | /**
81 | * 闲聊
82 | */
83 | val smallTalk = 0
84 |
85 | /**
86 | * 公告
87 | */
88 | val announcement = 1
89 |
90 | /**
91 | * 攻略
92 | */
93 | val strategy = 2
94 |
95 | /**
96 | * 开黑
97 | */
98 | val openBlack = 3
99 |
100 |
101 | }
102 |
103 |
104 | @NoArg
105 | data class ChannelDto(
106 | @SerializedName("name")
107 | val name: String,
108 | @SerializedName("type")
109 | val type: Int,
110 | @SerializedName("position")
111 | val position: Int,
112 | @SerializedName("parent_id")
113 | val parentId: String
114 | )
115 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/entity/ChannelPermissions.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 |
7 | @NoArg
8 | data class ChannelPermissions(
9 | @SerializedName("channel_id")
10 | val channelId: String,
11 | @SerializedName("user_id")
12 | val userId: String,
13 | @SerializedName("permissions")
14 | val permissions: String
15 | )
16 |
17 |
18 | class Permissions {
19 | companion object {
20 | /**
21 | * 可查看子频道
22 | */
23 | const val CHANNEL_VIEWABLE: Long = 1 shl 0
24 |
25 | /**
26 | * 可管理子频道
27 | */
28 | const val CHANNEL_MANAGEABLE: Long = 1 shl 1
29 | }
30 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/entity/Guild.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 |
7 | @NoArg
8 | data class Guild(
9 | @SerializedName("id")
10 | val id: String,
11 | @SerializedName("name")
12 | val name: String,
13 | @SerializedName("icon")
14 | val icon: String,
15 | @SerializedName("owner_id")
16 | val ownerId: String,
17 | @SerializedName("owner")
18 | val owner: Boolean,
19 | @SerializedName("member_count")
20 | val memberCount: Int,
21 | @SerializedName("max_members")
22 | val maxMembers: Int,
23 | @SerializedName("description")
24 | val description: String,
25 | @SerializedName("joined_at")
26 | val joinedAt: String,
27 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/entity/Member.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 |
7 | @NoArg
8 | data class Member(
9 | @SerializedName("user")
10 | val user: User?,
11 | @SerializedName("nick")
12 | val nick: String,
13 | @SerializedName("joined_at")
14 | val joinedAt: String,
15 | @SerializedName("roles")
16 | val roles: List,
17 | @SerializedName("deaf")
18 | val deaf: Boolean,
19 | @SerializedName("mute")
20 | val mute: Boolean,
21 | @SerializedName("pending")
22 | val pending: Boolean
23 | )
24 |
25 |
26 | @NoArg
27 | data class MemberWithGuildID(
28 | @SerializedName("guild_id")
29 | val guildId: String,
30 | @SerializedName("user")
31 | val user: User,
32 | @SerializedName("nick")
33 | val nick: String,
34 | @SerializedName("joined_at")
35 | val joinedAt: String,
36 | @SerializedName("roles")
37 | val roles: List
38 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/entity/Message.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 | import com.hcyacg.protocol.event.api.MessageArk
6 | import com.hcyacg.protocol.event.api.MessageAttachment
7 | import com.hcyacg.protocol.event.api.MessageEmbed
8 | import com.hcyacg.protocol.event.api.MessageReference
9 |
10 |
11 | @NoArg
12 | data class Message(
13 | @SerializedName("id")
14 | val id: String,
15 | @SerializedName("channel_id")
16 | val channelId: String,
17 | @SerializedName("guild_id")
18 | val guildId: String,
19 | @SerializedName("content")
20 | val content: String,
21 | @SerializedName("timestamp")
22 | // @Serializable(with = LocalDateTimeSerializer::class)
23 | val timestamp: String,
24 | @SerializedName("edited_timestamp")
25 | // @Serializable(with = LocalDateTimeSerializer::class)
26 | val editedTimestamp: String,
27 | @SerializedName("mention_everyone")
28 | val mentionEveryone: Boolean,
29 | @SerializedName("author")
30 | val author: User,
31 | @SerializedName("attachments")
32 | val attachments: MessageAttachment,
33 | @SerializedName("embeds")
34 | val embeds: MessageEmbed,
35 | @SerializedName("mentions")
36 | val mentions: User,
37 | @SerializedName("member")
38 | val member: Member,
39 | @SerializedName("ark")
40 | val ark: MessageArk,
41 | @SerializedName("message_reference")
42 | val messageReference: MessageReference,
43 | @SerializedName("seq_in_channel")
44 | val seqInChannel: String,
45 | @SerializedName("src_guild_id")
46 | val srcGuildId: String,
47 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/entity/MessageAudited.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.entity
2 | import com.google.gson.annotations.SerializedName
3 | import com.hcyacg.protocol.anno.NoArg
4 |
5 | @NoArg
6 | open class MessageAudited {
7 | @SerializedName("audit_id")
8 | lateinit var auditId: String
9 | @SerializedName("audit_time")
10 | lateinit var auditTime: String
11 | @SerializedName("channel_id")
12 | lateinit var channelId: String
13 | @SerializedName("create_time")
14 | lateinit var createTime: String
15 | @SerializedName("guild_id")
16 | lateinit var guildId: String
17 | @SerializedName("message_id")
18 | lateinit var messageId: String
19 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/entity/Role.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 |
7 | @NoArg
8 | data class Role(
9 | @SerializedName("id")
10 | val id: String,
11 | @SerializedName("name")
12 | val name: String,
13 | @SerializedName("color")
14 | val color: Long,
15 | @SerializedName("hoist")
16 | val hoist: Long,
17 | @SerializedName("number")
18 | val number: Long,
19 | @SerializedName("number_limit")
20 | val numberLimit: Int,
21 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/entity/Roles.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 |
7 | @NoArg
8 | data class Roles(
9 | @SerializedName("guild_id")
10 | val guildId: String,
11 | @SerializedName("roles")
12 | val roles: List,
13 | @SerializedName("number_limit")
14 | val numberLimit: Int,
15 | )
16 |
17 | /**
18 | *@param name 是否修改名称: 0-否, 1-是
19 | *@param color 是否修改颜色: 0-否, 1-是
20 | *@param hoist 是否修改在成员列表中单独展示: 0-否, 1-是
21 | */
22 |
23 | @NoArg
24 | data class Filter(
25 | @SerializedName("name")
26 | val name: Long,
27 | @SerializedName("color")
28 | val color: Long,
29 | @SerializedName("hoist")
30 | val hoist: Long,
31 | )
32 |
33 | /**
34 | *@param name 名称
35 | *@param color ARGB的HEX十六进制颜色值转换后的十进制数值
36 | *@param hoist 在成员列表中单独展示: 0-否, 1-是
37 | */
38 |
39 | @NoArg
40 | data class Info(
41 | @SerializedName("name")
42 | val name: String,
43 | @SerializedName("color")
44 | val color: Long,
45 | @SerializedName("hoist")
46 | val hoist: Long,
47 | )
48 |
49 |
50 |
51 | @NoArg
52 | data class RoleVo(
53 | @SerializedName("role_id")
54 | val roleId: Long,
55 | @SerializedName("role")
56 | val role: Role,
57 | )
58 |
59 | /**
60 | * @param filter 标识需要修改哪些字段
61 | * @param info 携带需要修改的字段内容
62 | */
63 |
64 | @NoArg
65 | data class RoleDto(
66 | @SerializedName("filter")
67 | val filter: Filter,
68 | @SerializedName("info")
69 | val info: Info,
70 | )
71 |
72 | /**
73 | * 系统自带的身份组
74 | */
75 |
76 | class DefaultRoles {
77 | companion object {
78 | /**
79 | * 全体成员
80 | */
81 | const val ALL_MEMBER: Long = 1
82 |
83 | /**
84 | * 管理员
85 | */
86 | const val ADMINISTRATOR: Long = 2
87 |
88 | /**
89 | * 群主/创建者
90 | */
91 | const val CREATOR: Long = 4
92 |
93 | /**
94 | * 子频道管理员
95 | */
96 | const val CHANNEL_MANAGER: Long = 5
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/entity/Schedule.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 | /**
7 | * 字段名 类型 描述
8 | id string 日程 id
9 | name string 日程名称
10 | description string 日程描述
11 | start_timestamp string 日程开始时间戳(ms)
12 | end_timestamp string 日程结束时间戳(ms)
13 | creator Member 创建者
14 | jump_channel_id string 日程开始时跳转到的子频道 id
15 | remind_type string 日程提醒类型,取值参考RemindType
16 | */
17 |
18 | @NoArg
19 | data class Schedule(
20 | @SerializedName("id")
21 | val id: String?,
22 | @SerializedName("name")
23 | val name: String,
24 | @SerializedName("description")
25 | val description: String,
26 | @SerializedName("start_timestamp")
27 | val startTimestamp: String,
28 | @SerializedName("end_timestamp")
29 | val endTimestamp: String,
30 | @SerializedName("creator")
31 | var creator: Member?,
32 | @SerializedName("jump_channel_id")
33 | val jumpChannelId: String,
34 | @SerializedName("remind_type")
35 | val remindType: String,
36 | )
37 |
38 | /**
39 | * 提醒类型 id 描述
40 | 0 不提醒
41 | 1 开始时提醒
42 | 2 开始前5分钟提醒
43 | 3 开始前15分钟提醒
44 | 4 开始前30分钟提醒
45 | 5 开始前60分钟提醒
46 | */
47 |
48 | class RemindType {
49 | companion object {
50 | /**
51 | * 不提醒
52 | */
53 | const val NO_REMINDER: String = "0"
54 |
55 | /**
56 | * 开始时提醒
57 | */
58 | const val START_WITH_REMINDER: String = "1"
59 |
60 | /**
61 | * 开始前5分钟提醒
62 | */
63 | const val FIVE_MINUTES_BEFORE_THE_START: String = "2"
64 |
65 | /**
66 | * 开始前10分钟提醒
67 | */
68 | const val TEN_MINUTES_BEFORE_THE_START: String = "3"
69 |
70 | /**
71 | * 开始前30分钟提醒
72 | */
73 | const val THIRTY_MINUTES_BEFORE_THE_START: String = "4"
74 |
75 | /**
76 | * 开始前60分钟提醒
77 | */
78 | const val SIXTY_MINUTES_BEFORE_THE_START: String = "5"
79 | }
80 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/entity/User.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 |
7 | @NoArg
8 | data class User(
9 | @SerializedName("bot")
10 | var bot: Boolean?,
11 | @SerializedName("id")
12 | val id: String?,
13 | @SerializedName("username")
14 | val username: String?,
15 | @SerializedName("avatar")
16 | val avatar: String?,
17 | @SerializedName("union_openid")
18 | val unionOpenid: String?,
19 | @SerializedName("union_user_account")
20 | val unionUserAccount: String?,
21 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/AudioActionEvent.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 |
7 | @NoArg
8 | data class AudioActionEvent(
9 | @SerializedName("guild_id")
10 | val guildId: String,
11 | @SerializedName("channel_id")
12 | val channelId: String,
13 | @SerializedName("audio_url")
14 | val audioUrl: String,
15 | @SerializedName("text")
16 | val text: String
17 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/ChannelEvent.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 |
7 | @NoArg
8 | data class ChannelEvent(
9 | @SerializedName("guild_id")
10 | val guildId: String,
11 | @SerializedName("id")
12 | val id: String,
13 | @SerializedName("name")
14 | val name: String,
15 | @SerializedName("op_user_id")
16 | val opUserId: String,
17 | @SerializedName("owner_id")
18 | val ownerId: String,
19 | @SerializedName("sub_type")
20 | val subType: Int,
21 | @SerializedName("type")
22 | val type: Int
23 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/GuildEvent.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 | @NoArg
7 | data class GuildEvent(
8 | @SerializedName("description")
9 | val description: String,
10 | @SerializedName("icon")
11 | val icon: String,
12 | @SerializedName("id")
13 | val id: String,
14 | @SerializedName("joined_at")
15 | val joinedAt: String,
16 | @SerializedName("max_members")
17 | val maxMembers: Int,
18 | @SerializedName("member_count")
19 | val memberCount: Int,
20 | @SerializedName("name")
21 | val name: String,
22 | @SerializedName("op_user_id")
23 | val opUserId: String,
24 | @SerializedName("owner_id")
25 | val ownerId: String
26 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/GuildMemberEvent.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 | import com.hcyacg.protocol.event.api.User
6 |
7 | @NoArg
8 | data class GuildMemberEvent(
9 | @SerializedName("guild_id")
10 | val guildId: String,
11 | @SerializedName("joined_at")
12 | val joinedAt: String,
13 | @SerializedName("nick")
14 | val nick: String,
15 | @SerializedName("op_user_id")
16 | val opUserId: String,
17 | @SerializedName("roles")
18 | val roles: List,
19 | @SerializedName("user")
20 | val user: User
21 | )
22 |
23 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/ReadyEvent.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 | import com.hcyacg.protocol.event.api.User
6 |
7 |
8 | @NoArg
9 |
10 | data class ReadyEvent(
11 | @SerializedName("session_id")
12 | val sessionId: String,
13 | @SerializedName("shard")
14 | val shard: List,
15 | @SerializedName("user")
16 | val user: User,
17 | @SerializedName("version")
18 | val version: Int
19 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/api/Author.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.api
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 | import com.hcyacg.protocol.constant.Constant
6 | import com.hcyacg.protocol.constant.Constant.Companion.proUrl
7 | import com.hcyacg.protocol.constant.Constant.Companion.threadLocal
8 | import com.hcyacg.protocol.utils.OkHttpUtils
9 |
10 | @NoArg
11 | open class Author{
12 | @SerializedName("avatar")
13 | lateinit var avatar: String
14 | @SerializedName("bot")
15 | var bot: Boolean = false
16 | @SerializedName("id")
17 | lateinit var id: String
18 | @SerializedName("username")
19 | lateinit var username: String
20 |
21 | private val guildInfo = "${proUrl}/guilds/{{guild_id}}"
22 | private val memberMute = "${guildInfo}/members/{{user_id}}/mute"
23 |
24 | private fun officeApiHeader(): MutableMap {
25 | return mutableMapOf(
26 | "Authorization" to Constant.botToken!!
27 | )
28 | }
29 |
30 | /**
31 | * 成员禁言
32 | * @param timestamp 时间戳 单位:秒
33 | */
34 | fun mute(timestamp: Long): Boolean {
35 | val url = memberMute.replace("{{guild_id}}", threadLocal.get()).replace("{{user_id}}", this.id)
36 | val json = "{\"mute_end_timestamp\": ${timestamp}}"
37 | val res = OkHttpUtils.patch(url, OkHttpUtils.addJson(json), officeApiHeader())
38 | Constant.logger.debug(res.code.toString())
39 | return res.code == 204
40 | }
41 |
42 | /**
43 | * 成员禁言
44 | * @param seconds 时间戳 单位:秒
45 | */
46 | fun mute(seconds: Int): Boolean {
47 | val guildId = threadLocal.get()
48 | val url = memberMute.replace("{{guild_id}}", guildId).replace("{{user_id}}", this.id)
49 | val json = "{\"mute_seconds\": ${seconds}}"
50 | val res = OkHttpUtils.patch(url, OkHttpUtils.addJson(json), officeApiHeader())
51 | Constant.logger.debug(res.code.toString())
52 | return res.code == 204
53 | }
54 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/api/Dms.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.api
2 | import com.google.gson.annotations.SerializedName
3 | import com.hcyacg.protocol.anno.NoArg
4 |
5 | @NoArg
6 | data class Dms(
7 | @SerializedName("channel_id")
8 | val channelId: String,
9 | @SerializedName("create_time")
10 | val createTime: String,
11 | @SerializedName("guild_id")
12 | val guildId: String
13 | )
14 |
15 | @NoArg
16 | data class DmsDto(
17 | @SerializedName("recipient_id")
18 | val recipient_id : String,
19 | @SerializedName("source_guild_id")
20 | val source_guild_id: String
21 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/api/Member.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.api
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 |
6 | data class Member(
7 | @SerializedName("joined_at")
8 | val joinedAt: String,
9 | @SerializedName("roles")
10 | val roles: List
11 | )
12 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/api/Message.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.api
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 |
6 | data class Message(
7 | @SerializedName("content")
8 | val content: String, //消息内容,文本内容,支持内嵌格式
9 | @SerializedName("embed")
10 | val embed: MessageEmbed, //embed 消息,一种特殊的 ark
11 | @SerializedName("ark")
12 | val ark: MessageArk, //ark 消息
13 | @SerializedName("image")
14 | val image: String, //图片url地址
15 | @SerializedName("msg_id")
16 | val msgId: String //要回复的消息id(Message.id), 在 AT_CREATE_MESSAGE 事件中获取。带了 msg_id 视为被动回复消息,否则视为主动推送消息
17 | )
18 |
19 |
20 | data class TextMessage(
21 | @SerializedName("content")
22 | val content: String, //消息内容,文本内容,支持内嵌格式
23 | @SerializedName("msg_id")
24 | val msgId: String //要回复的消息id(Message.id), 在 AT_CREATE_MESSAGE 事件中获取。带了 msg_id 视为被动回复消息,否则视为主动推送消息
25 | )
26 |
27 |
28 | data class initiativeTextMessage(
29 | @SerializedName("content")
30 | val content: String //消息内容,文本内容,支持内嵌格式
31 | )
32 |
33 |
34 | data class TextWithImageMessage(
35 | @SerializedName("content")
36 | val content: String, //消息内容,文本内容,支持内嵌格式
37 | @SerializedName("image")
38 | val image: String, //图片url地址
39 | @SerializedName("msg_id")
40 | val msgId: String //要回复的消息id(Message.id), 在 AT_CREATE_MESSAGE 事件中获取。带了 msg_id 视为被动回复消息,否则视为主动推送消息
41 | )
42 |
43 |
44 | data class initiativeTextWithImageMessage(
45 | @SerializedName("content")
46 | val content: String, //消息内容,文本内容,支持内嵌格式
47 | @SerializedName("image")
48 | val image: String //图片url地址
49 | )
50 |
51 |
52 |
53 | data class ImageMessage(
54 | @SerializedName("image")
55 | val image: String, //图片url地址
56 | @SerializedName("msg_id")
57 | val msgId: String //要回复的消息id(Message.id), 在 AT_CREATE_MESSAGE 事件中获取。带了 msg_id 视为被动回复消息,否则视为主动推送消息
58 | )
59 |
60 |
61 | data class initiativeImageMessage(
62 | @SerializedName("image")
63 | val image: String, //图片url地址
64 | )
65 |
66 |
67 |
68 | data class ArkMessage(
69 | @SerializedName("ark")
70 | val ark: MessageArk, //ark 消息
71 | @SerializedName("msg_id")
72 | val msgId: String //要回复的消息id(Message.id), 在 AT_CREATE_MESSAGE 事件中获取。带了 msg_id 视为被动回复消息,否则视为主动推送消息
73 | )
74 |
75 |
76 | data class initiativeArkMessage(
77 | @SerializedName("ark")
78 | val ark: MessageArk //ark 消息
79 | )
80 |
81 |
82 |
83 | data class EmbedMessage(
84 | @SerializedName("embed")
85 | val embed: MessageEmbed, //embed 消息,一种特殊的 ark
86 | @SerializedName("msg_id")
87 | val msgId: String //要回复的消息id(Message.id), 在 AT_CREATE_MESSAGE 事件中获取。带了 msg_id 视为被动回复消息,否则视为主动推送消息
88 | )
89 |
90 |
91 |
92 | data class initiativeEmbedMessage(
93 | @SerializedName("embed")
94 | val embed: MessageEmbed, //embed 消息,一种特殊的 ark
95 | )
96 |
97 | /**
98 | * embed
99 | *@param title 标题
100 | *@param description 描述
101 | *@param prompt 消息弹窗内容
102 | *@param timestamp 消息创建时间
103 | *@param fields MessageEmbedField 对象数组 消息创建时间
104 | */
105 |
106 | data class MessageEmbed(
107 | @SerializedName("title")
108 | val title: String, //标题
109 | @SerializedName("description")
110 | val description: String, //描述
111 | @SerializedName("prompt")
112 | val prompt: String, //消息弹窗内容
113 | @SerializedName("timestamp")
114 | // (with = LocalDateTimeSerializer::class)
115 | val timestamp: String, //消息创建时间
116 | @SerializedName("fields")
117 | val fields: MessageEmbedField //消息创建时间
118 | )
119 |
120 | /**
121 | *@param name 字段名
122 | *@param value 字段值
123 | */
124 |
125 | data class MessageEmbedField(
126 | @SerializedName("name")
127 | val name: String,
128 | @SerializedName("value")
129 | val value: String
130 | )
131 |
132 | /**
133 | *@param url 下载地址
134 | */
135 |
136 | data class MessageAttachment(
137 | @SerializedName("url")
138 | val url: String //下载地址
139 | )
140 |
141 | /**
142 | * @param templateId ark模板id(需要先申请) 23 链接+文本列表模板 24 文本+缩略图模板 37 大图模板
143 | * @param kv MessageAkrKv arkkv数组 kv值列表
144 | */
145 |
146 | data class MessageArk(
147 | @SerializedName("template_id")
148 | val templateId: Int,
149 | @SerializedName("kv")
150 | val kv: List
151 | )
152 |
153 | /**
154 | *@param key key
155 | *@param value value
156 | *@param obj MessageArkObj arkobj类型的数组 ark obj类型的列表
157 | */
158 |
159 | data class MessageArkKv(
160 | @SerializedName("key")
161 | val key: String,
162 | @SerializedName("value")
163 | val value: String?,
164 | @SerializedName("obj")
165 | val obj: List?
166 | )
167 |
168 | /**
169 | *@param objKv MessageArkObjKv objkv类型的数组 ark objkv列表
170 | */
171 |
172 | data class MessageArkObj(
173 | @SerializedName("obj_kv")
174 | val objKv: List
175 | )
176 |
177 | /**
178 | *@param key key
179 | *@param value value
180 | */
181 |
182 | data class MessageArkObjKv(
183 | @SerializedName("key")
184 | val key: String,
185 | @SerializedName("value")
186 | val value: String
187 | )
188 |
189 | data class MessageReference(
190 | @SerializedName("message_id")
191 | val messageId:String,
192 | @SerializedName("ignore_get_message_error")
193 | val ignoreGetMessageError : Boolean
194 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/api/User.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.api
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 |
6 | data class User(
7 | @SerializedName("bot")
8 | val bot: Boolean,
9 | @SerializedName("id")
10 | val id: String,
11 | @SerializedName("username")
12 | val username: String
13 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/message/AtMessageCreateEvent.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.message
2 |
3 | import com.hcyacg.protocol.anno.NoArg
4 |
5 |
6 | @NoArg
7 | class AtMessageCreateEvent : EventApi() {
8 |
9 |
10 | fun messageContent(): String {
11 | return content.replace(Regex("<@!\\d+>"), "")
12 | }
13 |
14 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/message/EventApi.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.message
2 |
3 | import com.google.gson.Gson
4 | import com.hcyacg.protocol.constant.Constant
5 | import com.hcyacg.protocol.constant.Constant.Companion.logger
6 | import com.hcyacg.protocol.constant.Constant.Companion.proUrl
7 | import com.hcyacg.protocol.entity.*
8 | import com.hcyacg.protocol.entity.Message
9 | import com.hcyacg.protocol.event.api.*
10 | import com.hcyacg.protocol.utils.OkHttpUtils
11 |
12 | open class EventApi : MessageEvent() {
13 | private val channel = "${proUrl}/channels/{{channel_id}}"
14 | private val sendMessage = "$channel/messages"
15 | private val sendAudio = "$channel/audio"
16 | private val recall = "$sendMessage/{{message_id}}"
17 |
18 | private val guildInfo = "$proUrl/guilds/{{guild_id}}"
19 | private val memberMute = "${guildInfo}/members/{{user_id}}/mute"
20 | private val mute = "$proUrl/guilds/{{guild_id}}/mute"
21 |
22 |
23 | private fun officeApiHeader(): MutableMap {
24 | return mutableMapOf(
25 | "Authorization" to Constant.botToken!!
26 | )
27 | }
28 |
29 | fun replyText(msg: String): Message {
30 | val url = sendMessage.replace("{{channel_id}}", this.channelId)
31 | val json = Gson().toJson(TextMessage(msg, this.id))
32 |
33 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
34 | logger.debug(res)
35 | return Gson().fromJson(res, Message::class.java)
36 | }
37 |
38 | fun sendText(msg: String): Message {
39 | val url = sendMessage.replace("{{channel_id}}", this.channelId)
40 | val json = Gson().toJson(initiativeTextMessage(msg))
41 |
42 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
43 | logger.debug(res)
44 | return Gson().fromJson(res, Message::class.java)
45 | }
46 |
47 | fun replyTextWithImage(msg: String, imageUrl: String): Message {
48 | val url = sendMessage.replace("{{channel_id}}", this.channelId)
49 | val json = Gson().toJson(TextWithImageMessage(msg, imageUrl, this.id))
50 |
51 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
52 | logger.debug(res)
53 | return Gson().fromJson(res, Message::class.java)
54 | }
55 |
56 | fun sendTextWithImage(msg: String, imageUrl: String): Message {
57 | val url = sendMessage.replace("{{channel_id}}", this.channelId)
58 | val json = Gson().toJson(initiativeTextWithImageMessage(msg, imageUrl))
59 |
60 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
61 | logger.debug(res)
62 | return Gson().fromJson(res, Message::class.java)
63 | }
64 |
65 | fun replyImage(imageUrl: String): Message {
66 | val url = sendMessage.replace("{{channel_id}}", this.channelId)
67 | val json = Gson().toJson(ImageMessage(imageUrl, this.id))
68 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
69 | logger.debug(res)
70 | return Gson().fromJson(res, Message::class.java)
71 | }
72 |
73 |
74 | fun sendImage(imageUrl: String): Message {
75 | val url = sendMessage.replace("{{channel_id}}", this.channelId)
76 | val json = Gson().toJson(initiativeImageMessage(imageUrl))
77 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
78 | logger.debug(res)
79 | return Gson().fromJson(res, Message::class.java)
80 | }
81 |
82 | fun replyArk(messageArk: MessageArk): Message {
83 | val url = sendMessage.replace("{{channel_id}}", this.channelId)
84 | val json = Gson().toJson(ArkMessage(messageArk, this.id))
85 |
86 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
87 | logger.debug(res)
88 | return Gson().fromJson(res, Message::class.java)
89 | }
90 |
91 | fun sendArk(messageArk: MessageArk): Message {
92 | val url = sendMessage.replace("{{channel_id}}", this.channelId)
93 | val json = Gson().toJson(initiativeArkMessage(messageArk))
94 |
95 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
96 | logger.debug(res)
97 | return Gson().fromJson(res, Message::class.java)
98 | }
99 |
100 | fun replyEmbed(messageEmbed: MessageEmbed): Message {
101 | val url = sendMessage.replace("{{channel_id}}", this.channelId)
102 | val json = Gson().toJson(EmbedMessage(messageEmbed, this.id))
103 |
104 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
105 | logger.debug(res)
106 | return Gson().fromJson(res, Message::class.java)
107 | }
108 |
109 | fun sendEmbed(messageEmbed: MessageEmbed): Message {
110 | val url = sendMessage.replace("{{channel_id}}", this.channelId)
111 | val json = Gson().toJson(initiativeEmbedMessage(messageEmbed))
112 |
113 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
114 | logger.debug(res)
115 | return Gson().fromJson(res, Message::class.java)
116 | }
117 |
118 | fun sendAudio(audioUrl: String, text: String, status: Int): Message {
119 | val url = sendAudio.replace("{{channel_id}}", this.channelId)
120 | val json = Gson().toJson(AudioControl(audioUrl, text, status))
121 |
122 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
123 | logger.debug(res)
124 | return Gson().fromJson(res, Message::class.java)
125 | }
126 |
127 |
128 | /**
129 | * 全局禁言
130 | * @param timestamp 时间戳 单位:秒
131 | */
132 | fun mute(timestamp: Long): Boolean {
133 | val url = mute.replace("{{guild_id}}", guildId)
134 | val json = "{\"mute_end_timestamp\": ${timestamp}}"
135 | val res = OkHttpUtils.patch(url, OkHttpUtils.addJson(json), officeApiHeader())
136 | logger.debug(res.code.toString())
137 | return res.code == 204
138 | }
139 |
140 | /**
141 | * 全局禁言
142 | * @param seconds 时间戳 单位:秒
143 | */
144 | fun mute(seconds: Int): Boolean {
145 | val url = mute.replace("{{guild_id}}", guildId)
146 | val json = "{\"mute_seconds\": ${seconds}}"
147 | val res = OkHttpUtils.patch(url, OkHttpUtils.addJson(json), officeApiHeader())
148 | logger.debug(res.code.toString())
149 | return res.code == 204
150 | }
151 |
152 |
153 | /**
154 | * 撤回当前消息
155 | * 用来撤回频道内的消息
156 | * 管理员可以撤回普通成员的消息
157 | * 频道主可以撤回所有人的消息
158 | */
159 | fun recall(): Boolean {
160 | val url = recall.replace("{{channel_id}}", channelId).replace("{{message_id}}", id)
161 | val res = OkHttpUtils.delete(url, mutableMapOf(), officeApiHeader())
162 | logger.debug(res.code.toString())
163 | return res.code == 200
164 | }
165 |
166 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/message/MessageAuditPassEvent.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.message
2 |
3 | import com.hcyacg.protocol.entity.MessageAudited
4 |
5 | class MessageAuditPassEvent : MessageAudited()
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/message/MessageAuditRejectEvent.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.message
2 |
3 | import com.hcyacg.protocol.entity.MessageAudited
4 |
5 | class MessageAuditRejectEvent : MessageAudited()
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/message/MessageCreateEvent.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.message
2 |
3 | import com.hcyacg.protocol.anno.NoArg
4 |
5 | @NoArg
6 | class MessageCreateEvent : EventApi() {
7 |
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/message/MessageEvent.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.message
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 | import com.hcyacg.protocol.event.api.Author
6 | import com.hcyacg.protocol.event.api.Member
7 |
8 | @NoArg
9 | open class MessageEvent {
10 | @SerializedName("attachments")
11 | var attachments: List = mutableListOf()
12 |
13 | @SerializedName("author")
14 | lateinit var author: Author
15 |
16 | @SerializedName("channel_id")
17 | lateinit var channelId: String
18 |
19 | @SerializedName("content")
20 | lateinit var content: String
21 |
22 | @SerializedName("guild_id")
23 | lateinit var guildId: String
24 |
25 | @SerializedName("id")
26 | lateinit var id: String
27 |
28 | @SerializedName("member")
29 | lateinit var member: Member
30 |
31 | @SerializedName("mentions")
32 | lateinit var mentions: List
33 |
34 | @SerializedName("timestamp")
35 | lateinit var timestamp: String
36 |
37 | fun isContentInitialized(): Boolean {
38 | return this::content.isInitialized
39 | }
40 | }
41 |
42 | /**
43 | * 附件 例:图片
44 | */
45 | @NoArg
46 | data class Attachment(
47 | @SerializedName("content_type")
48 | var content_type: String = "",
49 | @SerializedName("filename")
50 | var filename: String = "",
51 | @SerializedName("height")
52 | var height: Int = 0,
53 | @SerializedName("id")
54 | var id: String = "",
55 | @SerializedName("size")
56 | var size: Int = 0,
57 | @SerializedName("url")
58 | var url: String = "",
59 | @SerializedName("width")
60 | var width: Int = 0
61 | )
62 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/message/MessageReactionEvent.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.message
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 | @NoArg
7 |
8 | data class MessageReactionEvent(
9 | @SerializedName("channel_id")
10 | val channelId: String,
11 | @SerializedName("emoji")
12 | val emoji: Emoji,
13 | @SerializedName("guild_id")
14 | val guildId: String,
15 | @SerializedName("target")
16 | val target: Target,
17 | @SerializedName("user_id")
18 | val userId: String
19 | )
20 |
21 | @NoArg
22 |
23 | data class Emoji(
24 | @SerializedName("id")
25 | val id: String,
26 | @SerializedName("type")
27 | val type: Int
28 | )
29 |
30 | @NoArg
31 |
32 | data class Target(
33 | @SerializedName("id")
34 | val id: String,
35 | @SerializedName("type")
36 | val type: Int
37 | )
38 |
39 | fun getInfo(id: String): String {
40 | when (id) {
41 | "0" -> return "惊讶"
42 | "1" -> return "撇嘴"
43 | "2" -> return "色"
44 | "3" -> return "发呆"
45 | "4" -> return "得意"
46 | "5" -> return "流泪"
47 | "6" -> return "害羞"
48 | "7" -> return "闭嘴"
49 | "8" -> return "睡"
50 | "9" -> return "大哭"
51 | "10" -> return "尴尬"
52 | "11" -> return "发怒"
53 | "12" -> return "调皮"
54 | "13" -> return "呲牙"
55 | "14" -> return "微笑"
56 | "15" -> return "难过"
57 | "16" -> return "酷"
58 | "18" -> return "抓狂"
59 | "19" -> return "吐"
60 | "20" -> return "偷笑"
61 | "21" -> return "可爱"
62 | "22" -> return "白眼"
63 | "23" -> return "傲慢"
64 | "24" -> return "饥饿"
65 | "25" -> return "困"
66 | "26" -> return "惊恐"
67 | "27" -> return "流汗"
68 | "28" -> return "憨笑"
69 | "29" -> return "悠闲"
70 | "30" -> return "奋斗"
71 | "31" -> return "咒骂"
72 | "32" -> return "疑问"
73 | "33" -> return "嘘"
74 | "34" -> return "晕"
75 | "35" -> return "折磨"
76 | "36" -> return "衰"
77 | "37" -> return "骷髅"
78 | "38" -> return "敲打"
79 | "39" -> return "再见"
80 | "41" -> return "发抖"
81 | "42" -> return "爱情"
82 | "43" -> return "跳跳"
83 | "46" -> return "猪头"
84 | "49" -> return "拥抱"
85 | "53" -> return "蛋糕"
86 | "54" -> return "闪电"
87 | "55" -> return "炸弹"
88 | "56" -> return "刀"
89 | "57" -> return "足球"
90 | "59" -> return "便便"
91 | "60" -> return "咖啡"
92 | "61" -> return "饭"
93 | "63" -> return "玫瑰"
94 | "64" -> return "凋谢"
95 | "66" -> return "爱心"
96 | "67" -> return "心碎"
97 | "69" -> return "礼物"
98 | "74" -> return "太阳"
99 | "75" -> return "月亮"
100 | "76" -> return "赞"
101 | "77" -> return "踩"
102 | "78" -> return "握手"
103 | "79" -> return "胜利"
104 | "85" -> return "飞吻"
105 | "86" -> return "怄火"
106 | "89" -> return "西瓜"
107 | "96" -> return "冷汗"
108 | "97" -> return "擦汗"
109 | "98" -> return "抠鼻"
110 | "99" -> return "鼓掌"
111 | "100" -> return "糗大了"
112 | "101" -> return "坏笑"
113 | "102" -> return "左哼哼"
114 | "103" -> return "右哼哼"
115 | "104" -> return "哈欠"
116 | "105" -> return "鄙视"
117 | "106" -> return "委屈"
118 | "107" -> return "快哭了"
119 | "108" -> return "阴险"
120 | "109" -> return "亲亲"
121 | "110" -> return "吓"
122 | "111" -> return "可怜"
123 | "112" -> return "菜刀"
124 | "113" -> return "啤酒"
125 | "114" -> return "篮球"
126 | "115" -> return "乒乓"
127 | "116" -> return "示爱"
128 | "117" -> return "瓢虫"
129 | "118" -> return "抱拳"
130 | "119" -> return "勾引"
131 | "120" -> return "拳头"
132 | "121" -> return "差劲"
133 | "122" -> return "爱你"
134 | "123" -> return "NO"
135 | "124" -> return "OK"
136 | "125" -> return "转圈"
137 | "126" -> return "磕头"
138 | "127" -> return "回头"
139 | "128" -> return "跳绳"
140 | "129" -> return "挥手"
141 | "130" -> return "激动"
142 | "131" -> return "街舞"
143 | "132" -> return "献吻"
144 | "133" -> return "左太极"
145 | "134" -> return "右太极"
146 | "136" -> return "双喜"
147 | "137" -> return "鞭炮"
148 | "138" -> return "灯笼"
149 | "140" -> return "K歌"
150 | "144" -> return "喝彩"
151 | "145" -> return "祈祷"
152 | "146" -> return "爆筋"
153 | "147" -> return "棒棒糖"
154 | "148" -> return "喝奶"
155 | "151" -> return "飞机"
156 | "158" -> return "钞票"
157 | "168" -> return "药"
158 | "169" -> return "手枪"
159 | "171" -> return "茶"
160 | "172" -> return "眨眼睛"
161 | "173" -> return "泪奔"
162 | "174" -> return "无奈"
163 | "175" -> return "卖萌"
164 | "176" -> return "小纠结"
165 | "177" -> return "喷血"
166 | "178" -> return "斜眼笑"
167 | "179" -> return "doge"
168 | "180" -> return "惊喜"
169 | "181" -> return "骚扰"
170 | "182" -> return "笑哭"
171 | "183" -> return "我最美"
172 | "184" -> return "河蟹"
173 | "185" -> return "羊驼"
174 | "187" -> return "幽灵"
175 | "188" -> return "蛋"
176 | "190" -> return "菊花"
177 | "192" -> return "红包"
178 | "193" -> return "大笑"
179 | "194" -> return "不开心"
180 | "197" -> return "冷漠"
181 | "198" -> return "呃"
182 | "199" -> return "好棒"
183 | "200" -> return "拜托"
184 | "201" -> return "点赞"
185 | "202" -> return "无聊"
186 | "203" -> return "托脸"
187 | "204" -> return "吃"
188 | "205" -> return "送花"
189 | "206" -> return "害怕"
190 | "207" -> return "花痴"
191 | "208" -> return "小样儿"
192 | "210" -> return "飙泪"
193 | "211" -> return "我不看"
194 | "212" -> return "托腮"
195 | "214" -> return "啵啵"
196 | "215" -> return "糊脸"
197 | "216" -> return "拍头"
198 | "217" -> return "扯一扯"
199 | "218" -> return "舔一舔"
200 | "219" -> return "蹭一蹭"
201 | "220" -> return "拽炸天"
202 | "221" -> return "顶呱呱"
203 | "222" -> return "抱抱"
204 | "223" -> return "暴击"
205 | "224" -> return "开枪"
206 | "225" -> return "撩一撩"
207 | "226" -> return "拍桌"
208 | "227" -> return "拍手"
209 | "228" -> return "恭喜"
210 | "229" -> return "干杯"
211 | "230" -> return "嘲讽"
212 | "231" -> return "哼"
213 | "232" -> return "佛系"
214 | "233" -> return "掐一掐"
215 | "234" -> return "惊呆"
216 | "235" -> return "颤抖"
217 | "236" -> return "啃头"
218 | "237" -> return "偷看"
219 | "238" -> return "扇脸"
220 | "239" -> return "原谅"
221 | "240" -> return "喷脸"
222 | "241" -> return "生日快乐"
223 | "242" -> return "头撞击"
224 | "243" -> return "甩头"
225 | "244" -> return "扔狗"
226 | "245" -> return "加油必胜"
227 | "246" -> return "加油抱抱"
228 | "247" -> return "口罩护体"
229 | "260" -> return "办公"
230 | "261" -> return "忙碌"
231 | "262" -> return "心累"
232 | "263" -> return "沧桑"
233 | "264" -> return "捂脸"
234 | "265" -> return "刷手机"
235 | "266" -> return "嫌弃"
236 | "267" -> return "头秃"
237 | "268" -> return "问号"
238 | "269" -> return "暗中观察"
239 | "270" -> return "尴尬"
240 | "271" -> return "吃瓜"
241 | "272" -> return "呵呵"
242 | "273" -> return "柠檬"
243 | "274" -> return "南"
244 | "👗" -> return "连衣裙"
245 | "😏" -> return "哼哼"
246 | "😄" -> return "高兴"
247 | "😔" -> return "失落"
248 | "😍" -> return "花痴"
249 | "😉" -> return "媚眼"
250 | "☺" -> return "可爱"
251 | "😜" -> return "淘气"
252 | "😁" -> return "呲牙"
253 | "😝" -> return "吐舌"
254 | "😰" -> return "紧张"
255 | "😓" -> return "汗"
256 | "😚" -> return "亲亲"
257 | "😌" -> return "羞涩"
258 | "😊" -> return "嘿嘿"
259 | "❔" -> return "问号"
260 | "❕" -> return "叹号"
261 | "❌" -> return "错误"
262 | "☎" -> return "电话"
263 | "📷" -> return "相机"
264 | "📠" -> return "传真"
265 | "💻" -> return "电脑"
266 | "🎥" -> return "摄影机"
267 | "🎤" -> return "话筒"
268 | "🔫" -> return "手枪"
269 | "💿" -> return "光碟"
270 | "💓" -> return "爱心"
271 | "✨" -> return "闪光"
272 | "♣" -> return "扑克"
273 | "🀄" -> return "麻将"
274 | "〽" -> return "股票"
275 | "🎰" -> return "老虎机"
276 | "🚥" -> return "信号灯"
277 | "🚧" -> return "路障"
278 | "🎸" -> return "吉他"
279 | "💈" -> return "理发厅"
280 | "🛀" -> return "浴缸"
281 | "🚽" -> return "马桶"
282 | "🏠" -> return "家"
283 | "⛪" -> return "教堂"
284 | "⭕" -> return "正确"
285 | "⛄" -> return "雪人"
286 | "🌙" -> return "月亮"
287 | "☔" -> return "雨天"
288 | "☀" -> return "晴天"
289 | "☁" -> return "云朵"
290 | "💄" -> return "口红"
291 | "👟" -> return "鞋子"
292 | "👕" -> return "衣服"
293 | "👙" -> return "内衣"
294 | "👜" -> return "包"
295 | "🌂" -> return "雨伞"
296 | "👢" -> return "鞋子"
297 | "👠" -> return "高跟鞋"
298 | "🏦" -> return "银行"
299 | "👒" -> return "帽子"
300 | "🐭" -> return "老鼠"
301 | "🐳" -> return "海豚"
302 | "🐧" -> return "企鹅"
303 | "👼" -> return "天使"
304 | "🐯" -> return "老虎"
305 | "🐶" -> return "狗"
306 | "🐠" -> return "鱼"
307 | "🐛" -> return "虫"
308 | "👻" -> return "幽灵"
309 | "🐸" -> return "青蛙"
310 | "🐔" -> return "公鸡"
311 | "🐮" -> return "牛"
312 | "🐨" -> return "树懒"
313 | "🐤" -> return "小鸡"
314 | "💀" -> return "骷髅"
315 | "🐷" -> return "猪"
316 | "🐙" -> return "章鱼"
317 | "🐵" -> return "猴"
318 | "👦" -> return "男孩"
319 | "👧" -> return "女孩"
320 | "👨" -> return "爸爸"
321 | "👩" -> return "妈妈"
322 | "⛵" -> return "船"
323 | "🚌" -> return "公交"
324 | "🚀" -> return "火箭"
325 | "🐎" -> return "骑马"
326 | "🚲" -> return "自行车"
327 | "🚤" -> return "快艇"
328 | "🚗" -> return "汽车"
329 | "🚄" -> return "列车"
330 | "✈" -> return "飞机"
331 | "🔒" -> return "锁"
332 | "🔑" -> return "钥匙"
333 | "📫" -> return "文件"
334 | "♨" -> return "热"
335 | "💉" -> return "打针"
336 | "💩" -> return "便便"
337 | "👣" -> return "脚印"
338 | "🏥" -> return "医院"
339 | "⚡" -> return "闪电"
340 | "💤" -> return "睡觉"
341 | "💰" -> return "钱"
342 | "🏆" -> return "奖杯"
343 | "🔥" -> return "火"
344 | "🏨" -> return "酒店"
345 | "🏧" -> return "取款机"
346 | "🏪" -> return "超市"
347 | "🚹" -> return "男性"
348 | "💦" -> return "水"
349 | "🚺" -> return "女性"
350 | "💨" -> return "吹气"
351 | "📱" -> return "手机"
352 | "⭐" -> return "星星"
353 | "🔔" -> return "铃铛"
354 | "👑" -> return "皇冠"
355 | "💣" -> return "炸弹"
356 | "💍" -> return "戒指"
357 | "🐚" -> return "海螺"
358 | "🎈" -> return "气球"
359 | "🎀" -> return "蝴蝶结"
360 | "💝" -> return "礼物"
361 | "🌴" -> return "椰子树"
362 | "🎉" -> return "庆祝"
363 | "🌹" -> return "玫瑰"
364 | "🎄" -> return "圣诞树"
365 | "🚬" -> return "吸烟"
366 | "💊" -> return "药丸"
367 | "🍉" -> return "西瓜"
368 | "🍓" -> return "草莓"
369 | "🍊" -> return "橙子"
370 | "🍎" -> return "苹果"
371 | "☕" -> return "咖啡"
372 | "🍸" -> return "高脚杯"
373 | "🍻" -> return "干杯"
374 | "🍺" -> return "啤酒"
375 | "🍟" -> return "薯条"
376 | "🍳" -> return "煎蛋"
377 | "🙏" -> return "合十"
378 | "🍔" -> return "汉堡"
379 | "🍞" -> return "起司"
380 | "🎂" -> return "蛋糕"
381 | "🍣" -> return "寿司"
382 | "🍧" -> return "刨冰"
383 | "🍙" -> return "饭团"
384 | "🍜" -> return "拉面"
385 | "🍝" -> return "意面"
386 | "🍚" -> return "米饭"
387 | "👂" -> return "耳朵"
388 | "👄" -> return "嘴唇"
389 | "👃" -> return "鼻子"
390 | "👀" -> return "眼睛"
391 | "👇" -> return "向下"
392 | "👆" -> return "向上"
393 | "👉" -> return "向右"
394 | "👈" -> return "向左"
395 | "👌" -> return "好的"
396 | "👎" -> return "鄙视"
397 | "✌" -> return "胜利"
398 | "👏" -> return "鼓掌"
399 | "☝" -> return "向上"
400 | "👍" -> return "厉害"
401 | "👊" -> return "拳头"
402 | "💪" -> return "肌肉"
403 | "😂" -> return "激动"
404 | "😱" -> return "害怕"
405 | "😭" -> return "大哭"
406 | "😘" -> return "飞吻"
407 | "😳" -> return "瞪眼"
408 | "😒" -> return "不屑"
409 | else -> return "未知表情"
410 | }
411 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/message/directMessage/DirectMessage.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.message.directMessage
2 |
3 | import com.hcyacg.protocol.anno.NoArg
4 | import com.google.gson.annotations.SerializedName
5 |
6 |
7 | @NoArg
8 | open class DirectMessage{
9 | @SerializedName("author")
10 | lateinit var author: Author
11 | @SerializedName("channel_id")
12 | lateinit var channelId: String
13 | @SerializedName("content")
14 | lateinit var content: String
15 | @SerializedName("direct_message")
16 | var directMessage: Boolean = true
17 | @SerializedName("guild_id")
18 | lateinit var guildId: String
19 | @SerializedName("id")
20 | lateinit var id: String
21 | @SerializedName("member")
22 | lateinit var member: Member
23 | @SerializedName("seq")
24 | var seq: Int = 0
25 | @SerializedName("seq_in_channel")
26 | lateinit var seqInChannel: String
27 | @SerializedName("src_guild_id")
28 | lateinit var srcGuildId: String
29 | @SerializedName("timestamp")
30 | lateinit var timestamp: String
31 |
32 | fun isContentInitialized(): Boolean {
33 | return this::content.isInitialized
34 | }
35 | }
36 |
37 | data class Author(
38 | @SerializedName("avatar")
39 | val avatar: String,
40 | @SerializedName("id")
41 | val id: String,
42 | @SerializedName("username")
43 | val username: String
44 | )
45 |
46 | data class Member(
47 | @SerializedName("joined_at")
48 | val joinedAt: String
49 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/message/directMessage/DirectMessageCreateEvent.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.message.directMessage
2 |
3 | import com.hcyacg.protocol.anno.NoArg
4 |
5 | @NoArg
6 | open class DirectMessageCreateEvent : DmsApi()
7 |
8 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/event/message/directMessage/DmsApi.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.event.message.directMessage
2 |
3 | import com.google.gson.Gson
4 | import com.hcyacg.protocol.constant.Constant
5 | import com.hcyacg.protocol.constant.Constant.Companion.botToken
6 | import com.hcyacg.protocol.constant.Constant.Companion.logger
7 | import com.hcyacg.protocol.constant.Constant.Companion.proUrl
8 | import com.hcyacg.protocol.entity.Message
9 | import com.hcyacg.protocol.event.api.*
10 | import com.hcyacg.protocol.utils.OkHttpUtils
11 |
12 | open class DmsApi : DirectMessage() {
13 |
14 | private val sendMessage = "${proUrl}/dms/{{guild_id}}/messages"
15 | private val dms = "${proUrl}/users/@me/dms"
16 | private val recall = "${proUrl}/dms/{{guild_id}}/messages/{{message_id}}"
17 |
18 | private fun officeApiHeader(): MutableMap {
19 | return mutableMapOf(
20 | "Authorization" to Constant.botToken!!
21 | )
22 | }
23 |
24 | /**
25 | * 创建私聊会话
26 | */
27 | private fun createDms(authorId:String, guildId:String):Dms{
28 | val json = Gson().toJson(DmsDto(authorId,guildId))
29 | val res = OkHttpUtils.postJson(dms, OkHttpUtils.addJson(json), officeApiHeader())
30 | logger.debug(res)
31 | return Gson().fromJson(res, Dms::class.java)
32 | }
33 |
34 | fun replyText(msg: String): Message {
35 | val url = sendMessage.replace("{{guild_id}}", this.guildId)
36 | val json = Gson().toJson(TextMessage(msg, this.id))
37 |
38 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
39 | logger.debug(res)
40 | return Gson().fromJson(res, Message::class.java)
41 | }
42 |
43 | fun sendText(authorId:String,guildId:String,msg: String): Message {
44 | val createDms = createDms(authorId, guildId)
45 | val url = sendMessage.replace("{{guild_id}}", createDms.guildId)
46 | val json = Gson().toJson(initiativeTextMessage(msg))
47 |
48 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
49 | logger.debug(res)
50 | return Gson().fromJson(res, Message::class.java)
51 | }
52 |
53 | fun replyTextWithImage(msg: String, imageUrl: String): Message {
54 | val url = sendMessage.replace("{{guild_id}}", this.guildId)
55 | val json = Gson().toJson(TextWithImageMessage(msg, imageUrl, this.id))
56 |
57 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
58 | logger.debug(res)
59 | return Gson().fromJson(res, Message::class.java)
60 | }
61 |
62 | fun sendTextWithImage(authorId:String,guildId:String,msg: String, imageUrl: String): Message {
63 | val createDms = createDms(authorId, guildId)
64 | val url = sendMessage.replace("{{guild_id}}", createDms.guildId)
65 | val json = Gson().toJson(initiativeTextWithImageMessage(msg, imageUrl))
66 |
67 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
68 | logger.debug(res)
69 | return Gson().fromJson(res, Message::class.java)
70 | }
71 |
72 | fun replyImage(imageUrl: String): Message {
73 | val url = sendMessage.replace("{{guild_id}}", this.guildId)
74 | val json = Gson().toJson(ImageMessage(imageUrl, this.id))
75 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
76 | logger.debug(res)
77 | return Gson().fromJson(res, Message::class.java)
78 | }
79 |
80 |
81 | fun sendImage(authorId:String,guildId:String,imageUrl: String): Message {
82 | val createDms = createDms(authorId, guildId)
83 | val url = sendMessage.replace("{{guild_id}}", createDms.guildId)
84 | val json = Gson().toJson(initiativeImageMessage(imageUrl))
85 | val res = OkHttpUtils.postJson(url, OkHttpUtils.addJson(json), officeApiHeader())
86 | logger.debug(res)
87 | return Gson().fromJson(res, Message::class.java)
88 | }
89 |
90 | /**
91 | * 撤回当前消息
92 | * 用来撤回频道内的消息
93 | * 管理员可以撤回普通成员的消息
94 | * 频道主可以撤回所有人的消息
95 | */
96 | fun recall(guildId:String,messageId:String): Boolean {
97 | val url = recall.replace("{{guild_id}}", guildId).replace("{{message_id}}", messageId)
98 | val res = OkHttpUtils.delete(url, mutableMapOf(), officeApiHeader())
99 | logger.debug(res.code.toString())
100 | return res.code == 200
101 | }
102 |
103 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/internal/BaseBotClient.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.internal
2 |
3 | /**
4 | * 机器人客户端父类
5 | */
6 | internal interface BaseBotClient {
7 |
8 | fun reconnect()
9 |
10 | fun sendMessage(text: String)
11 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/internal/BaseBotListener.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.internal
2 |
3 | import okhttp3.WebSocketListener
4 |
5 | /**
6 | * 机器人监听父类
7 | */
8 | abstract class BaseBotListener : WebSocketListener() {
9 |
10 | lateinit var reconnect: () -> Unit
11 |
12 | fun reconnect(func: () -> Unit) {
13 | reconnect = func
14 | }
15 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/internal/config/IdentifyConfig.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.internal.config
2 |
3 | import com.hcyacg.protocol.internal.entity.D
4 | import com.hcyacg.protocol.internal.entity.Properties
5 |
6 |
7 | data class IdentifyConfig(
8 | val token: String,
9 | val shards: Int = 1,
10 | var index: Int = 0,
11 | val intents: Intents = Intents(),
12 | val properties: Properties = Properties()
13 | ) {
14 | fun toIdentifyOperationData(): D {
15 | return D(
16 | intents = intents.toIntentsValue(),
17 | properties = properties,
18 | shard = mutableListOf(index, shards),
19 | token = token
20 | )
21 | }
22 | }
23 |
24 |
25 | data class Intents(
26 | val guilds: Boolean = true,
27 | val guildMembers: Boolean = true,
28 | val directMessage: Boolean = true,
29 | val audioAction: Boolean = true,
30 | val forum: Boolean = true,
31 | val atMessages: Boolean = true,
32 | val messages: Boolean = false,
33 | val guildMessageReactions: Boolean = true,
34 | val messageAudit:Boolean = true
35 | ) {
36 | fun toIntentsValue(): Long {
37 | return ((if (guilds) 1.shl(0) else 0)
38 | + (if (guildMembers) 1.shl(1) else 0)
39 | + (if (messages) 1.shl(9) else 0)
40 | + (if (guildMessageReactions) 1.shl(10) else 0)
41 | + (if (directMessage) 1.shl(12) else 0)
42 | + (if (messageAudit) 1.shl(27) else 0)
43 | + (if (forum) 1.shl(28) else 0)
44 | + (if (audioAction) 1.shl(29) else 0)
45 | + (if (atMessages) 1.shl(30) else 0))
46 | .toLong()
47 | }
48 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/internal/entity/AccessWithFragmentedWss.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.internal.entity
2 |
3 | import com.hcyacg.protocol.anno.NoArg
4 | import com.google.gson.annotations.SerializedName
5 |
6 | /**
7 | * 获取带分片 WSS 接入点 时返回的实体对象
8 | * 字段名 类型 描述
9 | * url string WebSocket 的连接地址
10 | * shards int 建议的 shard 数
11 | * session_start_limit SessionStartLimit 创建Session限制信息
12 | *
13 | */
14 |
15 | @NoArg
16 | data class AccessWithFragmentedWss(
17 | @SerializedName("session_start_limit")
18 | var sessionStartLimit: SessionStartLimit,
19 | @SerializedName("shards")
20 | var shards: Int,
21 | @SerializedName("url")
22 | var url: String
23 | )
24 |
25 | /**
26 | * 字段名 类型 描述
27 | * total int 每 24 小时可创建 Session 数
28 | * remaining int 目前还可以创建的 Session 数
29 | * reset_after int 重置计数的剩余时间(ms)
30 | * max_concurrency int 每 5s 可以创建的 Session 数
31 | */
32 |
33 | @NoArg
34 | data class SessionStartLimit(
35 | @SerializedName("max_concurrency")
36 | var maxConcurrency: Int,
37 | @SerializedName("remaining")
38 | var remaining: Int,
39 | @SerializedName("reset_after")
40 | var resetAfter: Int,
41 | @SerializedName("total")
42 | var total: Int
43 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/internal/entity/Dispatch.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.internal.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.internal.enums.DispatchEnums
5 |
6 |
7 | data class Dispatch(
8 | @SerializedName("s")
9 | val seq: Long,
10 | @SerializedName("t")
11 | val type: DispatchEnums,
12 | @SerializedName("d")
13 | val d: T
14 | ) : Operation(0)
15 |
16 |
17 | data class DispatchType(
18 | @SerializedName("t")
19 | val type: DispatchEnums,
20 | @SerializedName("s")
21 | val seq: Long,
22 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/internal/entity/Heartbeat.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.internal.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 |
6 | data class Heartbeat(
7 | @SerializedName("d")
8 | val count: Long
9 | ) : Operation(1)
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/internal/entity/Identify.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.internal.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 |
6 | data class Identify(
7 | @SerializedName("d")
8 | var d: D,
9 | ) : Operation(2) {
10 | constructor(token: String) : this(D(token = token))
11 | }
12 |
13 |
14 | data class D(
15 | @SerializedName("intents")
16 | var intents: Long = 1610612739L,
17 | @SerializedName("properties")
18 | var properties: Properties = Properties(),
19 | @SerializedName("shard")
20 | var shard: List = listOf(0, 1),
21 | @SerializedName("token")
22 | var token: String
23 | )
24 |
25 |
26 | data class Properties(
27 | @SerializedName("\$browser")
28 | var browser: String = "okhttp",
29 | @SerializedName("\$device")
30 | var device: String = "tencent-guild-protocol by Nekoer",
31 | @SerializedName("\$os")
32 | var os: String? = System.getProperty("os.name").split(" ").firstOrNull() ?: "unknown"
33 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/internal/entity/Operation.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.internal.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 |
6 | open class Operation(
7 | @SerializedName("op")
8 | val op: Int
9 | )
10 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/internal/entity/Resume.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.internal.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 |
6 |
7 | data class Resume(
8 | @SerializedName("d")
9 | val d: ResumeData
10 | ) : Operation(6)
11 |
12 |
13 | data class ResumeData(
14 | @SerializedName("seq")
15 | val seq: Long,
16 | @SerializedName("session_id")
17 | val sessionId: String,
18 | @SerializedName("token")
19 | val token: String
20 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/internal/entity/UniversalWssAccess.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.internal.entity
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.hcyacg.protocol.anno.NoArg
5 |
6 | /**
7 | * 获取通用 WSS 接入点 时返回的实体对象
8 | * 段名 类型 描述
9 | * url string WebSocket 的连接地址
10 | */
11 |
12 | @NoArg
13 | data class UniversalWssAccess(
14 | @SerializedName("url")
15 | var url: String = ""
16 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/internal/enums/DispatchEnums.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.internal.enums
2 |
3 | /**
4 | * 事件种类
5 | */
6 | enum class DispatchEnums {
7 | READY, // 准备
8 | RESUMED, // 恢复
9 | GUILD_MEMBER_ADD, // 新用户加入频道
10 | GUILD_MEMBER_UPDATE, // 暂无
11 | GUILD_MEMBER_REMOVE, // 用户离开频道
12 | GUILD_CREATE, // 当机器人加入新guild时
13 | GUILD_UPDATE, // 当guild资料发生变更时
14 | GUILD_DELETE, // 当机器人退出guild时
15 | CHANNEL_CREATE, // 子频道被创建
16 | CHANNEL_UPDATE, // 子频道信息变更
17 | CHANNEL_DELETE, // 子频道被删除
18 | AT_MESSAGE_CREATE, // 艾特机器人
19 | MESSAGE_CREATE, // 私域不需要艾特
20 |
21 | MESSAGE_REACTION_ADD, // 为消息添加表情表态
22 | MESSAGE_REACTION_REMOVE, // 为消息删除表情表态
23 | DIRECT_MESSAGE_CREATE, // 当收到用户发给机器人的私信消息时
24 |
25 | MESSAGE_AUDIT_PASS, // 消息审核通过
26 | MESSAGE_AUDIT_REJECT, // 消息审核不通过
27 |
28 | THREAD_CREATE, // 当用户创建主题时
29 | THREAD_UPDATE, // 当用户更新主题时
30 | THREAD_DELETE, // 当用户删除主题时
31 | POST_CREATE, // 当用户创建帖子时
32 | POST_DELETE, // 当用户删除帖子时
33 | REPLY_CREATE, // 当用户回复评论时
34 | REPLY_DELETE, // 当用户回复评论时
35 |
36 | AUDIO_START, // 音频开始播放时
37 | AUDIO_FINISH, // 音频播放结束时
38 | AUDIO_ON_MIC, // 上麦时
39 | AUDIO_OFF_MIC, // 下麦时
40 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/internal/enums/OPCodeEnums.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.internal.enums
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 |
6 | /*
7 | CODE 名称 客户端操作 描述
8 | 0 Dispatch Receive 服务端进行消息推送
9 | 1 Heartbeat Send/Receive 客户端或服务端发送心跳
10 | 2 Identify Send 客户端发送鉴权
11 | 6 Resume Send 客户端回复会话
12 | 7 Reconnect Receive 服务端通知客户端重新连接
13 | 9 Invalid Session Receive 当identify或resume的时候,如果参数有错,服务端会返回该消息
14 | 11 Heartbeat ACK Receive 当发送心跳成功之后,就会收到该消息
15 | */
16 |
17 |
18 | enum class OPCodeEnums(
19 | val code: Int,
20 | val description: String,
21 | ) {
22 |
23 | @SerializedName("Dispatch")
24 | DISPATCH(0, "服务端进行消息推送"),
25 |
26 | @SerializedName("Heartbeat")
27 | HEARTBEAT(1, "客户端或服务端发送心跳"),
28 |
29 | @SerializedName("Identify")
30 | IDENTIFY(2, "客户端发送鉴权"),
31 |
32 | @SerializedName("Resume")
33 | RESUME(6, "客户端回复会话"),
34 |
35 | @SerializedName("Reconnect")
36 | RECONNECT(7, "服务端通知客户端重新连接"),
37 |
38 | @SerializedName("Invalid_Session")
39 | INVALID_SESSION(9, "当identify或resume的时候,如果参数有错,服务端会返回该消息"),
40 |
41 | @SerializedName("Hello")
42 | HELLO(10, "心跳参数"),
43 |
44 | @SerializedName("Heartbeat_ACK")
45 | HEARTBEAT_ACK(11, "当发送心跳成功之后,就会收到该消息"),
46 | UNKNOWN(-1, "未知")
47 | ;
48 |
49 | companion object {
50 | private val codeMap = values().associateBy { it.code }
51 | fun getOPCodeByCode(code: Int): OPCodeEnums {
52 | return codeMap[code] ?: UNKNOWN
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/utils/OkHttpUtils.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.utils
2 |
3 | import com.google.gson.Gson
4 | import okhttp3.*
5 | import okhttp3.MediaType.Companion.toMediaType
6 | import okhttp3.RequestBody.Companion.asRequestBody
7 | import okhttp3.RequestBody.Companion.toRequestBody
8 | import okio.ByteString
9 | import java.io.File
10 | import java.io.IOException
11 | import java.io.InputStream
12 | import java.io.UnsupportedEncodingException
13 | import java.net.URLEncoder
14 | import java.util.*
15 | import java.util.concurrent.TimeUnit
16 | import java.util.regex.Pattern
17 |
18 | object OkHttpUtils {
19 | private val MEDIA_JSON: MediaType = "application/json;charset=utf-8".toMediaType()
20 | private val MEDIA_STREAM: MediaType = "application/octet-stream".toMediaType()
21 | private val MEDIA_X_JSON: MediaType = "text/x-json".toMediaType()
22 | private val MEDIA_ENCRYPTED_JSON: MediaType = "application/encrypted-json;charset=UTF-8".toMediaType()
23 | private val MEDIA_TEXT: MediaType = "text/plain;charset=UTF-8".toMediaType()
24 | private const val TIME_OUT = 10L
25 | private val okHttpClient: OkHttpClient by lazy {
26 | OkHttpClient.Builder().followRedirects(false).followSslRedirects(false).connectTimeout(10L, TimeUnit.SECONDS)
27 | .readTimeout(10L, TimeUnit.SECONDS).build()
28 | }
29 |
30 | private fun emptyHeaders(): Headers {
31 | return addSingleHeader("user-agent", UA.PC.getValue())
32 | }
33 |
34 | @JvmOverloads
35 | @Throws(IOException::class)
36 | operator fun get(url: String, headers: Headers = emptyHeaders()): Response {
37 | val request: Request = Request.Builder().url(url).headers(headers).build()
38 | return okHttpClient.newCall(request).execute()
39 | }
40 |
41 | @Throws(IOException::class)
42 | operator fun get(url: String, map: Map): Response {
43 | return OkHttpUtils[url, addHeaders(map)]
44 | }
45 |
46 | @JvmOverloads
47 | @Throws(IOException::class)
48 | fun post(url: String, requestBody: RequestBody, headers: Headers = emptyHeaders()): Response {
49 | val request: Request = Request.Builder().url(url).post(requestBody).headers(headers).build()
50 | return okHttpClient.newCall(request).execute()
51 | }
52 |
53 | @JvmName("post1")
54 | @JvmOverloads
55 | @Throws(IOException::class)
56 | fun post(
57 | url: String,
58 | map: Map = mutableMapOf(),
59 | headers: Headers = emptyHeaders()
60 | ): Response {
61 | return post(url, mapToFormBody(map), headers)
62 | }
63 |
64 | @Throws(IOException::class)
65 | fun post(url: String, map: Map, headerMap: Map): Response {
66 | return post(url, mapToFormBody(map), addHeaders(headerMap))
67 | }
68 |
69 | @Throws(IOException::class)
70 | fun post(url: String, requestBody: RequestBody, headerMap: Map): Response {
71 | return post(url, requestBody, addHeaders(headerMap))
72 | }
73 |
74 | @Throws(IOException::class)
75 | fun post(url: String, map: Map): Response {
76 | return post(url, map, emptyHeaders())
77 | }
78 |
79 | @JvmOverloads
80 | @Throws(IOException::class)
81 | fun put(url: String, requestBody: RequestBody, headers: Headers): Response {
82 | val request: Request = Request.Builder().url(url).put(requestBody).headers(headers).build()
83 | return okHttpClient.newCall(request).execute()
84 | }
85 |
86 | @Throws(IOException::class)
87 | fun put(url: String, map: Map): Response {
88 | return put(url, mapToFormBody(map), emptyHeaders())
89 | }
90 |
91 | @Throws(IOException::class)
92 | fun put(url: String, map: Map, headers: Map): Response {
93 | return put(url, mapToFormBody(map), addHeaders(headers))
94 | }
95 |
96 | @Throws(IOException::class)
97 | fun put(url: String, map: Map, headers: Headers): Response {
98 | return put(url, mapToFormBody(map), headers)
99 | }
100 |
101 | @Throws(IOException::class)
102 | private fun delete(url: String, requestBody: RequestBody, headers: Headers = emptyHeaders()): Response {
103 | val request: Request = Request.Builder().url(url).delete(requestBody).headers(headers).build()
104 | return okHttpClient.newCall(request).execute()
105 | }
106 |
107 | @JvmOverloads
108 | @Throws(IOException::class)
109 | fun delete(url: String, map: Map, headers: Headers = emptyHeaders()): Response {
110 | return delete(url, mapToFormBody(map), headers)
111 | }
112 |
113 | @Throws(IOException::class)
114 | fun delete(url: String, map: Map, headerMap: Map): Response {
115 | return delete(url, mapToFormBody(map), addHeaders(headerMap))
116 | }
117 |
118 | @Throws(IOException::class)
119 | fun delete(url: String, requestBody: RequestBody, headerMap: Map): Response {
120 | return delete(url, requestBody, addHeaders(headerMap))
121 | }
122 |
123 | @JvmOverloads
124 | @Throws(IOException::class)
125 | fun patch(url: String, requestBody: RequestBody, headers: Headers = emptyHeaders()): Response {
126 | val request: Request = Request.Builder().url(url).patch(requestBody).headers(headers).build()
127 | return okHttpClient.newCall(request).execute()
128 | }
129 |
130 | @JvmOverloads
131 | @Throws(IOException::class)
132 | fun patch(url: String, map: Map, headers: Headers = emptyHeaders()): Response {
133 | return patch(url, mapToFormBody(map), headers)
134 | }
135 |
136 | @Throws(IOException::class)
137 | fun patch(url: String, map: Map, headerMap: Map): Response {
138 | return patch(url, mapToFormBody(map), addHeaders(headerMap))
139 | }
140 |
141 | @Throws(IOException::class)
142 | fun patch(url: String, requestBody: RequestBody, headerMap: Map): Response {
143 | return patch(url, requestBody, addHeaders(headerMap))
144 | }
145 |
146 | @Throws(IOException::class)
147 | fun getStr(response: Response): String {
148 | return (Objects.requireNonNull(response.body) as ResponseBody).string()
149 | }
150 |
151 | @Throws(IOException::class)
152 | fun getJson(response: Response): String {
153 | val str = getStr(response)
154 | return str
155 | }
156 |
157 | @Throws(IOException::class)
158 | fun getBytes(url: String): ByteArray {
159 | return getBytes(url, emptyHeaders())
160 | }
161 |
162 | @Throws(IOException::class)
163 | fun getBytes(url: String, headers: Headers): ByteArray {
164 | val response = OkHttpUtils[url, headers]
165 | return getBytes(response)
166 | }
167 |
168 | @Throws(IOException::class)
169 | fun getBytes(url: String, headers: Map): ByteArray {
170 | val response = OkHttpUtils[url, headers]
171 | return getBytes(response)
172 | }
173 |
174 | @Throws(IOException::class)
175 | fun getBytes(response: Response): ByteArray {
176 | return (Objects.requireNonNull(response.body) as ResponseBody).bytes()
177 | }
178 |
179 | fun getByteStream(response: Response): InputStream {
180 | return (Objects.requireNonNull(response.body) as ResponseBody).byteStream()
181 | }
182 |
183 | @Throws(IOException::class)
184 | fun getByteStream(url: String, headers: Headers): InputStream {
185 | val response = OkHttpUtils[url, headers]
186 | return getByteStream(response)
187 | }
188 |
189 | @Throws(IOException::class)
190 | fun getByteStream(url: String, headers: Map): InputStream {
191 | val response = OkHttpUtils[url, headers]
192 | return getByteStream(response)
193 | }
194 |
195 | @Throws(IOException::class)
196 | fun getByteStream(url: String): InputStream {
197 | return getByteStream(url, emptyHeaders())
198 | }
199 |
200 | @Throws(IOException::class)
201 | private fun getByteStr(response: Response): ByteString {
202 | return (Objects.requireNonNull(response.body) as ResponseBody).byteString()
203 | }
204 |
205 | @Throws(IOException::class)
206 | fun getByteStr(url: String, headers: Headers): ByteString {
207 | val response = OkHttpUtils[url, headers]
208 | return getByteStr(response)
209 | }
210 |
211 | @Throws(IOException::class)
212 | fun getByteStr(url: String, headers: Map): ByteString {
213 | val response = OkHttpUtils[url, headers]
214 | return getByteStr(response)
215 | }
216 |
217 | @Throws(IOException::class)
218 | fun getByteStr(url: String): ByteString {
219 | return getByteStr(url, emptyHeaders())
220 | }
221 |
222 | private fun getIs(response: Response): InputStream {
223 | return (Objects.requireNonNull(response.body) as ResponseBody).byteStream()
224 | }
225 |
226 | @Throws(IOException::class)
227 | fun getStr(url: String, headers: Headers): String {
228 | val response = OkHttpUtils[url, headers]
229 | return getStr(response)
230 | }
231 |
232 | @Throws(IOException::class)
233 | fun getStr(url: String, headers: Map): String {
234 | val response = OkHttpUtils[url, headers]
235 | return getStr(response)
236 | }
237 |
238 | @Throws(IOException::class)
239 | fun getStr(url: String): String {
240 | val response = OkHttpUtils[url, emptyHeaders()]
241 | return getStr(response)
242 | }
243 |
244 | @Throws(IOException::class)
245 | fun getJson(url: String, headers: Headers): String {
246 | val response = OkHttpUtils[url, headers]
247 | return getJson(response)
248 | }
249 |
250 | @Throws(IOException::class)
251 | fun getJson(url: String, map: Map): String {
252 | return getJson(url, addHeaders(map))
253 | }
254 |
255 | @Throws(IOException::class)
256 | fun getJson(url: String): String {
257 | val response = OkHttpUtils[url, emptyHeaders()]
258 | return getJson(response)
259 | }
260 |
261 | @Throws(IOException::class)
262 | fun postStr(url: String, requestBody: RequestBody, headers: Headers): String {
263 | val response = post(url, requestBody, headers)
264 | return getStr(response)
265 | }
266 |
267 | @Throws(IOException::class)
268 | fun postStr(url: String, requestBody: RequestBody, headers: Map): String {
269 | val response = post(url, requestBody, headers)
270 | return getStr(response)
271 | }
272 |
273 | @Throws(IOException::class)
274 | fun postStr(url: String, requestBody: RequestBody): String {
275 | val response = post(url, requestBody, emptyHeaders())
276 | return getStr(response)
277 | }
278 |
279 | @Throws(IOException::class)
280 | fun postJson(url: String, requestBody: RequestBody, headers: Headers): String {
281 | val response = post(url, requestBody, headers)
282 | return getJson(response)
283 | }
284 |
285 | @Throws(IOException::class)
286 | fun postJson(url: String, requestBody: RequestBody, headers: Map): String {
287 | val response = post(url, requestBody, headers)
288 | return getJson(response)
289 | }
290 |
291 | @Throws(IOException::class)
292 | fun postJson(url: String, requestBody: RequestBody): String {
293 | val response = post(url, requestBody, emptyHeaders())
294 | return getJson(response)
295 | }
296 |
297 | private fun mapToFormBody(map: Map): RequestBody {
298 | val builder = FormBody.Builder()
299 | val var2: Iterator<*> = map.entries.iterator()
300 | while (var2.hasNext()) {
301 | val (key, value) = var2.next() as Map.Entry<*, *>
302 | builder.add(key as String, value as String)
303 | }
304 | return builder.build()
305 | }
306 |
307 | @JvmOverloads
308 | @Throws(IOException::class)
309 | fun postStr(url: String, map: Map, headers: Headers = emptyHeaders()): String {
310 | val response = post(url, mapToFormBody(map), headers)
311 | return getStr(response)
312 | }
313 |
314 | @Throws(IOException::class)
315 | fun postStr(url: String, map: Map, headers: Map): String {
316 | val response = post(url, mapToFormBody(map), headers)
317 | return getStr(response)
318 | }
319 |
320 | @JvmOverloads
321 | @Throws(IOException::class)
322 | fun deleteStr(url: String, map: Map, headers: Headers = emptyHeaders()): String {
323 | val response = delete(url, mapToFormBody(map), headers)
324 | return getStr(response)
325 | }
326 |
327 | @Throws(IOException::class)
328 | fun deleteStr(url: String, map: Map, headers: Map): String {
329 | val response = delete(url, mapToFormBody(map), headers)
330 | return getStr(response)
331 | }
332 |
333 | @Throws(IOException::class)
334 | fun postJson(url: String, map: Map, headers: Headers): String {
335 | val str = postStr(url, map, headers)
336 | return Gson().toJson(str)
337 | }
338 |
339 | @Throws(IOException::class)
340 | fun postJson(url: String, map: Map, headers: Map): String {
341 | val str = postStr(url, map, headers)
342 | return Gson().toJson(str)
343 | }
344 |
345 | @Throws(IOException::class)
346 | fun postJson(url: String, map: Map): String {
347 | val str = postStr(url, map, emptyHeaders())
348 | return Gson().toJson(str)
349 | }
350 |
351 | @Throws(IOException::class)
352 | fun patchJson(url: String, map: Map): String {
353 | val str = getStr(patch(url, map, emptyHeaders()))
354 | return Gson().toJson(str)
355 | }
356 |
357 | @Throws(IOException::class)
358 | fun patchJson(url: String, requestBody: RequestBody, headers: Map): String {
359 | val str = getStr(patch(url, requestBody, headers))
360 | return Gson().toJson(str)
361 | }
362 |
363 |
364 | @Throws(IOException::class)
365 | fun deleteJson(url: String, map: Map, headers: Headers): String {
366 | val str = deleteStr(url, map, headers)
367 | return Gson().toJson(str)
368 | }
369 |
370 | @Throws(IOException::class)
371 | fun deleteJson(url: String, map: Map, headers: Map): String {
372 | val str = deleteStr(url, map, headers)
373 | return Gson().toJson(str)
374 | }
375 |
376 | @Throws(IOException::class)
377 | fun deleteJson(url: String, map: Map): String {
378 | val str = deleteStr(url, map, emptyHeaders())
379 | return Gson().toJson(str)
380 | }
381 |
382 |
383 |
384 | fun addJson(jsonStr: String): RequestBody {
385 | return jsonStr.toRequestBody(MEDIA_JSON)
386 | }
387 |
388 | fun addText(text: String): RequestBody {
389 | return text.toRequestBody(MEDIA_TEXT)
390 | }
391 |
392 | fun addBody(text: String, contentType: String): RequestBody {
393 | val mediaType: MediaType = contentType.toMediaType()
394 | return text.toRequestBody(mediaType)
395 | }
396 |
397 | // fun addJson(JsonElement: String): RequestBody {
398 | // return JsonElement.toRequestBody(MEDIA_JSON)
399 | // }
400 |
401 | fun addEncryptedJson(str: String): RequestBody {
402 | return str.toRequestBody(MEDIA_ENCRYPTED_JSON)
403 | }
404 |
405 | fun addSingleHeader(name: String, value: String): Headers {
406 | return Headers.Builder().add(name, value).build()
407 | }
408 |
409 | fun addHeaders(cookie: String?, referer: String = "", userAgent: String = UA.PC.getValue()): Headers {
410 | val header = Headers.Builder()
411 | if (cookie != null) {
412 | header.add("cookie", cookie)
413 | }
414 | return header.add("referer", referer).add("user-agent", userAgent).build()
415 | }
416 |
417 | fun addHeaders(cookie: String, referer: String, ua: UA): Headers {
418 | return addHeaders(cookie, referer, ua.getValue())
419 | }
420 |
421 | fun addHeaders(cookie: String, referer: String): Headers {
422 | return addHeaders(cookie, referer, UA.PC.getValue())
423 | }
424 |
425 | fun addHeaders(map: Map): Headers {
426 | val builder = Headers.Builder()
427 | val var2: Iterator<*> = map.entries.iterator()
428 | while (var2.hasNext()) {
429 | val (key, value) = var2.next() as Map.Entry<*, *>
430 | builder.add(key as String, value as String)
431 | }
432 | return builder.build()
433 | }
434 |
435 | fun addHeader(): Headers.Builder {
436 | return Headers.Builder()
437 | }
438 |
439 | fun addUA(ua: UA): Headers {
440 | return addSingleHeader("user-agent", ua.getValue())
441 | }
442 |
443 | fun addUA(ua: String): Headers {
444 | return addSingleHeader("user-agent", ua)
445 | }
446 |
447 | fun addCookie(cookie: String): Headers {
448 | return addSingleHeader("cookie", cookie)
449 | }
450 |
451 | fun addReferer(url: String): Headers {
452 | return addSingleHeader("referer", url)
453 | }
454 |
455 | fun addStream(byteString: ByteString): RequestBody {
456 | return byteString.toRequestBody(MEDIA_STREAM)
457 | }
458 |
459 | @Throws(IOException::class)
460 | fun addStream(url: String): RequestBody {
461 | return addStream(getByteStr(url))
462 | }
463 |
464 |
465 | fun getCookie(cookie: String, name: String): String? {
466 | var arr = cookie.split("; ").toTypedArray()
467 | if (arr.isEmpty()) {
468 | arr = cookie.split(";").toTypedArray()
469 | }
470 | val var3 = arr
471 | val var4 = arr.size
472 | for (var5 in 0 until var4) {
473 | val str = var3[var5]
474 | val newArr = str.split("=").toTypedArray()
475 | if (newArr.size > 1 && newArr[0].trim { it <= ' ' } == name) {
476 | return str.substring(str.indexOf(61.toChar()) + 1)
477 | }
478 | }
479 | return null
480 | }
481 |
482 | fun getCookie(cookie: String, vararg name: String): Map {
483 | return name.toList().map { it to getCookie(cookie, it) }.filter { it.second != null }
484 | .associate { it.first to it.second!! }
485 | }
486 |
487 | fun cookieToMap(cookie: String): Map {
488 | val map: MutableMap = mutableMapOf()
489 | val arr = cookie.split(";").toTypedArray()
490 | val var4 = arr.size
491 | for (var5 in 0 until var4) {
492 | val str = arr[var5]
493 | val newArr = str.split("=").toTypedArray()
494 | if (newArr.size > 1) {
495 | map[newArr[0].trim { it <= ' ' }] = newArr[1].trim { it <= ' ' }
496 | }
497 | }
498 | return map
499 | }
500 |
501 | private fun fileNameByUrl(url: String): String {
502 | val index = url.lastIndexOf("/")
503 | return url.substring(index + 1)
504 | }
505 |
506 | fun getStreamBody(file: File): RequestBody {
507 | return file.asRequestBody(MEDIA_STREAM)
508 | }
509 |
510 | fun getStreamBody(file: File, contentType: String): RequestBody {
511 | return file.asRequestBody(contentType.toMediaType())
512 | }
513 |
514 |
515 | fun getStreamBody(fileName: String, bytes: ByteArray): RequestBody {
516 | return bytes.toRequestBody(MEDIA_STREAM)
517 | }
518 |
519 | fun getStreamBody(bytes: ByteArray): RequestBody {
520 | return getStreamBody(UUID.randomUUID().toString(), bytes)
521 | }
522 |
523 | fun urlParams(map: Map): String {
524 | val sb = StringBuilder()
525 | val var2: Iterator> = map.entries.iterator()
526 | while (var2.hasNext()) {
527 | val (key, value) = var2.next()
528 | try {
529 | sb.append(key).append("=").append(URLEncoder.encode(value, "utf-8")).append("&")
530 | } catch (var5: UnsupportedEncodingException) {
531 | var5.printStackTrace()
532 | }
533 | }
534 | return sb.toString()
535 | }
536 |
537 | fun urlParamsEn(map: Map): String {
538 | val sb = StringBuilder()
539 | val var2: Iterator> = map.entries.iterator()
540 | while (var2.hasNext()) {
541 | val (key, value) = var2.next()
542 | sb.append(key).append("=").append(value).append("&")
543 | }
544 | return sb.toString()
545 | }
546 |
547 | }
548 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/utils/ScheduleUtils.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.utils
2 |
3 | import kotlinx.coroutines.*
4 | import java.util.*
5 | import kotlin.coroutines.CoroutineContext
6 |
7 |
8 | object ScheduleUtils : CoroutineScope {
9 | fun loopEvent(process: suspend () -> Unit, start: Date, period: Long): Timer {
10 | val timer = Timer()
11 | val task = object : TimerTask() {
12 | override fun run() {
13 | launch {
14 | process()
15 | }
16 | }
17 | }
18 | timer.schedule(task, start, period)
19 | return timer
20 | }
21 |
22 | override val coroutineContext: CoroutineContext =
23 | Dispatchers.IO + CoroutineName(this::class.simpleName!!) + SupervisorJob()
24 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/hcyacg/protocol/utils/UA.kt:
--------------------------------------------------------------------------------
1 | package com.hcyacg.protocol.utils
2 |
3 | enum class UA(private val value: String) {
4 | PC("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"),
5 | CHROME_91(
6 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
7 | ),
8 | MOBILE("Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Mobile Safari/537.36"), QQ(
9 | "Mozilla/5.0 (Linux; Android 10; V1914A Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/045132 Mobile Safari/537.36 V1_AND_SQ_8.3.0_1362_YYB_D QQ/8.3.0.4480 NetType/4G WebP/0.3.0 Pixel/1080 StatusBarHeight/85 SimpleUISwitch/0 QQTheme/1000"
10 | ),
11 | QQ2("Mozilla/5.0 (Linux; Android 10; V1914A Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045224 Mobile Safari/537.36 V1_AND_SQ_8.3.9_1424_YYB_D QQ/8.3.9.4635 NetType/4G WebP/0.3.0 Pixel/1080 StatusBarHeight/85 SimpleUISwitch/0 QQTheme/1000"), QQ3(
12 | "Mozilla/5.0 (Linux; Android 10; M2002J9E Build/QKQ1.191222.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045230 Mobile Safari/537.36 V1_AND_SQ_8.4.5_1468_YYB_D QQ/8.4.5.4745 NetType/4G WebP/0.3.0 Pixel/1080 StatusBarHeight/70 SimpleUISwitch/0 QQTheme/1000 InMagicWin/0"
13 | ),
14 | OPPO("Mozilla/5.0 (Linux; Android 11; RMX3031 Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.120 Mobile Safari/537.36 oppostore/200902 ColorOS/V11.1 brand/realme model/RMX3031"), PIXEL(
15 | "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Mobile Safari/537.36"
16 | );
17 |
18 | override fun toString(): String {
19 | return "UA.$name(value=$value)"
20 | }
21 |
22 | fun getValue(): String {
23 | return this.value
24 | }
25 | }
--------------------------------------------------------------------------------
/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | ${CONSOLE_LOG_PATTERN}
13 |
14 |
15 |
16 | DEBUG
17 |
18 |
19 |
20 |
21 |
22 | ${log.path}/log_info.log
23 |
24 |
25 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
26 | UTF-8
27 |
28 |
29 |
30 |
31 | ${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log
32 |
33 | 100MB
34 |
35 |
36 | 15
37 |
38 |
39 |
40 | INFO
41 |
42 |
43 |
44 |
45 |
46 | ${log.path}/log_warn.log
47 |
48 |
49 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
50 | UTF-8
51 |
52 |
53 |
54 | ${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log
55 |
56 | 100MB
57 |
58 |
59 | 15
60 |
61 |
62 |
63 | warn
64 | ACCEPT
65 | DENY
66 |
67 |
68 |
69 |
70 |
71 | ${log.path}/log_error.log
72 |
73 |
74 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
75 | UTF-8
76 |
77 |
78 |
79 | ${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log
80 |
81 | 100MB
82 |
83 |
84 | 15
85 |
86 |
87 |
88 | ERROR
89 | ACCEPT
90 | DENY
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------