634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published
637 | by the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
662 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # OneBot Client
5 |
6 | _✨ 基于java开发的 [OneBot](https://github.com/howmanybots/onebot/blob/master/README.md) 协议客户端✨_
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | 文档 |
18 | QuickStart
19 |
20 |
21 |
22 | # QuickStart
23 |
24 | ### 使用api进行请求
25 | ```java
26 | public class WebSocketClientTest {
27 | public static OneBotClient onebot;
28 | public static void sendApi(String[] args) {
29 | onebot = OneBotClient.create(new BotConfig("ws://127.0.0.1:8080"))//创建websocket客户端
30 | .open()//连接onebot服务端
31 | .registerEvents(new EventListeners());//注册事件监听器
32 |
33 | onebot.getBot().sendGroupMsg(123456, MsgUtils.builder().text("123").build(), true);//发送群消息
34 | GroupMemberInfoResp sender = onebot.getBot().getGroupMemberInfo(123456, 123456, false).getData();//获取响应的群成员信息
35 | System.out.println(sender.toString());//打印
36 | }
37 | }
38 | ```
39 |
40 | ### 事件监听示例
41 | ```java
42 | public class EventListeners implements Listener{
43 | @SubscribeEvent
44 | public void onGroup(GroupMessageEvent event){
45 | System.out.println(event);
46 | }
47 | }
48 |
49 | public class WebSocketClientTest {
50 | public static OneBotClient onebot;
51 | public static void main(String[] args){
52 | onebot = OneBotClient.create(new BotConfig("ws://127.0.0.1:8080"))//创建websocket客户端
53 | .open()//连接onebot服务端
54 | .registerEvents(new EventListeners());//注册事件监听器
55 | }
56 |
57 | public static void stopped() {
58 | if (onebot != null) onebot.close();
59 | }
60 | }
61 | ```
62 |
63 | # Client
64 |
65 | OneBot-Client 以 [OneBot-v11](https://github.com/howmanybots/onebot/tree/master/v11/specs) 标准协议进行开发,兼容所有支持正向WebSocket的OneBot协议端
66 |
67 | | 项目地址 | 核心作者 | 备注 |
68 | |-----------------------------------------------------------------------------------|----------------|-----------------------------------------------------------------------|
69 | | [Overflow](https://github.com/MrXiaoM/Overflow) | MrXiaoM | 实现 mirai 的无缝迁移 |
70 | | [Lagrange.Core](https://github.com/LagrangeDev/Lagrange.Core) | NepPure | C#实现 By Konata.Core |
71 | | [OpenShamrock](https://github.com/whitechi73/OpenShamrock) | whitechi73 | Xposed框架hook实现 |
72 | | [Gensokyo](https://github.com/Hoshinonyaruko/Gensokyo) | Hoshinonyaruko | 基于官方api 轻量 原生跨平台 |
73 | | [LLOnebot](https://github.com/LLOneBot/LLOneBot) | linyuchen | 使用[LiteLoaderQQNT](https://github.com/LiteLoaderQQNT/LiteLoaderQQNT) |
74 |
75 | # Credits
76 |
77 | * [OneBot](https://github.com/botuniverse/onebot)
78 |
79 | # License
80 |
81 | This product is licensed under the GNU General Public License version 3. The license is as published by the Free
82 | Software Foundation published at https://www.gnu.org/licenses/gpl-3.0.html.
83 |
84 | Alternatively, this product is licensed under the GNU Lesser General Public License version 3 for non-commercial use.
85 | The license is as published by the Free Software Foundation published at https://www.gnu.org/licenses/lgpl-3.0.html.
86 |
87 | Feel free to contact us if you have any questions about licensing or want to use the library in a commercial closed
88 | source product.
89 |
90 | # Thanks
91 |
92 | Thanks [JetBrains](https://www.jetbrains.com/?from=onebot-client) Provide Free License Support OpenSource Project
93 |
94 | [
](https://www.jetbrains.com/?from=onebot-client)
95 |
96 | ## Stargazers over time
97 |
98 | [](https://starchart.cc/cnlimiter/onebot-client)
99 |
100 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'maven-publish'
4 | id 'com.github.johnrengelman.shadow' version '7.1.2'
5 | }
6 |
7 | version = project.client_version
8 | group = project.maven_group
9 |
10 | def targetJavaVersion = 8
11 |
12 | tasks.withType(JavaCompile).configureEach {
13 | it.options.encoding = "UTF-8"
14 | if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
15 | it.options.release = targetJavaVersion
16 | }
17 | }
18 |
19 | java {
20 | def javaVersion = JavaVersion.toVersion(targetJavaVersion)
21 | if (JavaVersion.current() < javaVersion) {
22 | toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
23 | }
24 | archivesBaseName = project.archives_base_name
25 |
26 | }
27 |
28 | configurations {
29 | shadow
30 | implementation.extendsFrom shadow
31 | }
32 |
33 |
34 | repositories {
35 | mavenLocal()
36 | maven { url = "https://maven.nova-committee.cn/releases"}
37 | maven { url = "https://repo.papermc.io/repository/maven-public/" }
38 | mavenCentral()
39 | }
40 |
41 |
42 | dependencies {
43 | compileOnly("org.projectlombok:lombok:1.18.24")
44 | compileOnly("com.google.code.gson:gson:2.10.1")
45 | compileOnly("org.jetbrains:annotations:24.0.1")
46 |
47 | compileOnly("org.apache.logging.log4j:log4j-api:2.19.0")
48 | compileOnly("org.apache.logging.log4j:log4j-core:2.19.0")
49 |
50 | testImplementation("org.apache.logging.log4j:log4j-core:2.19.0")
51 | testImplementation("com.google.code.gson:gson:2.10.1")
52 | testCompileOnly("org.projectlombok:lombok:1.18.24")
53 | testRuntimeOnly 'org.slf4j:slf4j-simple:2.0.6'
54 |
55 | shadow "net.kyori:event-api:${eventbus_version}"
56 | shadow "net.kyori:event-method:${eventbus_version}"
57 | shadow "cn.evole.onebot:OneBot-SDK:${sdk_version}"
58 | shadow "org.java-websocket:Java-WebSocket:${websocket_version}"
59 |
60 | annotationProcessor("org.projectlombok:lombok:1.18.24")
61 |
62 | }
63 |
64 | artifacts {
65 | archives jar
66 | archives shadowJar
67 | }
68 |
69 |
70 | shadowJar {
71 | project.configurations.shadow.setTransitive(false);
72 | configurations = [project.configurations.shadow]
73 | relocate 'org.java_websocket', "cn.evole.onebot.sdk.websocket"
74 | relocate 'net.kyori.event', "cn.evole.onebot.eventbus"
75 | dependencies {
76 | exclude(dependency('org.slf4j:slf4j-api:2.0.6'))
77 | }
78 | archiveClassifier = ""
79 | archiveBaseName.set(project.archives_base_name)
80 | archiveVersion.set(project.client_version)
81 |
82 | }
83 |
84 | publishing {
85 | publications {
86 | shadow(MavenPublication) { publication ->
87 | project.shadow.component(publication)
88 | version = "${project.client_version}"
89 | artifactId = "${project.archives_base_name}"
90 | groupId = "${project.maven_group}"
91 | }
92 | }
93 |
94 | repositories {
95 | if (System.getenv('MAVEN_USERNAME') != null && System.getenv('MAVEN_PASSWORD') != null) {
96 | maven {
97 | name 's3'
98 | url = 'https://maven.nova-committee.cn/s3'
99 |
100 | credentials {
101 | username System.getenv('MAVEN_USERNAME')
102 | password System.getenv('MAVEN_PASSWORD')
103 | }
104 | }
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Done to increase the memory available to gradle.
2 | org.gradle.jvmargs=-Xmx1G
3 |
4 |
5 | maven_group=cn.evole.onebot
6 | archives_base_name=OneBot-Client
7 | client_version=0.4.3
8 |
9 | java_version=8
10 | sdk_version=0.3.1
11 | eventbus_version=3.0.0
12 | websocket_version=1.6.0
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cnlimiter/onebot-client/3cba0a110a6ee01b479c36ee4958e16dc6929f05/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original 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 POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/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:
--------------------------------------------------------------------------------
1 | rootProject.name = 'onebot-client'
2 |
3 |
--------------------------------------------------------------------------------
/src/main/java/cn/evole/onebot/client/OneBotClient.java:
--------------------------------------------------------------------------------
1 | package cn.evole.onebot.client;
2 |
3 | import cn.evole.onebot.client.connection.WSClient;
4 | import cn.evole.onebot.client.core.Bot;
5 | import cn.evole.onebot.client.core.BotConfig;
6 | import cn.evole.onebot.client.instances.action.ActionFactory;
7 | import cn.evole.onebot.client.instances.event.EventFactory;
8 | import cn.evole.onebot.client.instances.event.EventsBusImpl;
9 | import cn.evole.onebot.client.instances.event.MsgHandlerImpl;
10 | import cn.evole.onebot.client.interfaces.EventsBus;
11 | import cn.evole.onebot.client.interfaces.Listener;
12 | import lombok.Getter;
13 | import org.apache.logging.log4j.LogManager;
14 | import org.apache.logging.log4j.Logger;
15 |
16 | import java.net.URI;
17 | import java.util.concurrent.ExecutorService;
18 | import java.util.concurrent.Executors;
19 | import java.util.concurrent.TimeUnit;
20 |
21 | /**
22 | * @Project: onebot-client
23 | * @Author: cnlimiter
24 | * @CreateTime: 2024/1/26 22:58
25 | * @Description:
26 | */
27 |
28 | @Getter
29 | public final class OneBotClient {
30 | private final ExecutorService eventExecutor = Executors.newFixedThreadPool(2, r -> new Thread(r, "OneBot Event"));
31 | private final ExecutorService wsPool = Executors.newFixedThreadPool(2, r -> new Thread(r, "OneBot WS"));
32 | private final Logger logger;
33 | private final BotConfig config;
34 | private final EventsBus eventsBus;
35 | private final MsgHandlerImpl msgHandler;
36 | private final EventFactory eventFactory;
37 | private final ActionFactory actionFactory;
38 |
39 | private WSClient ws = null;
40 | private Bot bot = null;
41 |
42 | private OneBotClient(BotConfig config) {
43 | this.logger = LogManager.getLogger("OneBot Client");
44 | this.config = config;
45 | this.eventsBus = new EventsBusImpl(this);
46 | this.msgHandler = new MsgHandlerImpl(this);
47 | this.eventFactory = new EventFactory(this);
48 | this.actionFactory = new ActionFactory(this);
49 | }
50 |
51 | public static OneBotClient create(BotConfig config){
52 | return new OneBotClient(config);
53 | }
54 |
55 | public static OneBotClient create(BotConfig config, Listener... listeners){
56 | return new OneBotClient(config).registerEvents(listeners);
57 | }
58 |
59 | public OneBotClient open() {
60 | StringBuilder url = new StringBuilder();
61 | wsPool.execute(() -> {
62 | url.append(config.getUrl())
63 | .append(config.isMirai() ? "/all?verifyKey=" + config.getToken() + "&qq=" + config.getBotId() : "");
64 | try {
65 | ws = new WSClient(this, URI.create(url.toString()));
66 | ws.connect();
67 | bot = ws.createBot();
68 | } catch (Exception e) {
69 | logger.error("▌ §c{}连接错误,请检查服务端是否开启 §a┈━═☆", URI.create(url.toString()));
70 | }
71 | });
72 | return this;
73 | }
74 |
75 | public boolean close() {
76 | try {
77 | ws.getTimer().cancel();
78 | ws.closeBlocking();
79 | } catch (InterruptedException e) {
80 | logger.error("▌ §c{} 打断关闭进程的未知错误 §a┈━═☆", e);
81 | ws = null;
82 | }
83 | return threadStop(eventExecutor) && threadStop(wsPool);
84 | }
85 |
86 | public OneBotClient registerEvents(Listener... listeners){
87 | for (Listener c : listeners){
88 | getEventsBus().register(c);
89 | }
90 | return this;
91 | }
92 |
93 | private boolean threadStop(ExecutorService service){
94 | if (!service.isShutdown()) {
95 | service.shutdown();
96 | try {
97 | return service.awaitTermination(2, TimeUnit.SECONDS);
98 | } catch (InterruptedException e) {
99 | logger.error("▌ §c{} 打断关闭进程的未知错误 §a┈━═☆", e);
100 | service.shutdownNow();
101 | Thread.currentThread().interrupt();
102 | }
103 | }
104 | return false;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/cn/evole/onebot/client/annotations/EventBus.java:
--------------------------------------------------------------------------------
1 | package cn.evole.onebot.client.annotations;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * @Project: onebot-client
10 | * @Author: cnlimiter
11 | * @CreateTime: 2024/2/20 10:12
12 | * @Description:
13 | */
14 | @Target({ElementType.TYPE})
15 | @Retention(RetentionPolicy.RUNTIME)
16 | public @interface EventBus {
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/cn/evole/onebot/client/annotations/SubscribeEvent.java:
--------------------------------------------------------------------------------
1 | package cn.evole.onebot.client.annotations;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * @Project: onebot-client
10 | * @Author: cnlimiter
11 | * @CreateTime: 2024/1/26 23:09
12 | * @Description:
13 | */
14 |
15 | @Target({ElementType.METHOD})
16 | @Retention(RetentionPolicy.RUNTIME)
17 | public @interface SubscribeEvent {
18 | /**
19 | * 内部注册的处理器优先度更高
20 | * @return 是否内部注册
21 | */
22 | boolean internal() default false;
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/cn/evole/onebot/client/connection/WSClient.java:
--------------------------------------------------------------------------------
1 | package cn.evole.onebot.client.connection;
2 |
3 | import cn.evole.onebot.client.OneBotClient;
4 | import cn.evole.onebot.client.core.Bot;
5 | import lombok.Getter;
6 | import org.java_websocket.client.WebSocketClient;
7 | import org.java_websocket.handshake.ServerHandshake;
8 |
9 | import java.net.ConnectException;
10 | import java.net.URI;
11 | import java.util.Timer;
12 | import java.util.TimerTask;
13 |
14 | /**
15 | * @Project: onebot-client
16 | * @Author: cnlimiter
17 | * @CreateTime: 2023/4/4 2:20
18 | * @Description:
19 | */
20 | public class WSClient extends WebSocketClient {
21 | @Getter private final Timer timer = new Timer();
22 | private final OneBotClient client;
23 | private int reconnectTimes = 1;
24 | public WSClient(OneBotClient client, URI uri) {
25 | super(uri);
26 | this.client = client;
27 | this.setConnectionLostTimeout(0);
28 | addHeader("User-Agent", "OneBot Client v4");
29 | addHeader("x-client-role", "Universal"); // koishi-adapter-onebot 需要这个字段
30 | if (!client.getConfig().getToken().isEmpty()) addHeader("Authorization", "Bearer " + client.getConfig().getToken());
31 | if (client.getConfig().getBotId() != 0) addHeader("X-Self-ID", String.valueOf(client.getConfig().getBotId()));
32 | }
33 |
34 | public Bot createBot(){
35 | return new Bot(this, client.getActionFactory());
36 | }
37 |
38 | @Override
39 | public void onOpen(ServerHandshake handshake) {
40 | client.getLogger().info("▌ §c已连接到服务器 {} §a┈━═☆", getURI());
41 | reconnectTimes = 1;
42 | //handshake.iterateHttpFields().forEachRemaining(s -> System.out.println(s + ": " + handshake.getFieldValue(s)));
43 | }
44 |
45 | @Override
46 | public void onMessage(String message) {
47 | client.getMsgHandler().handle(message);
48 | }
49 |
50 | @Override
51 | public void onClose(int code, String reason, boolean remote) {
52 | if (client.getConfig().isReconnect() && remote && reconnectTimes <= client.getConfig().getReconnectMaxTimes()) {
53 | reconnectWebsocket();
54 | } else client.getLogger().info("▌ §c服务器{}因{}已关闭", getURI(), reason);
55 |
56 | }
57 |
58 | @Override
59 | public void onError(Exception ex) {
60 | if (ex instanceof ConnectException && ex.getMessage().equals("Connection refused: connect")
61 | && client.getConfig().isReconnect()
62 | && reconnectTimes <= client.getConfig().getReconnectMaxTimes()) {
63 | reconnectWebsocket();
64 | } else client.getLogger().error("▌ §c服务器{}出现错误{}或未连接§a┈━═☆", getURI(), ex.getLocalizedMessage());
65 | }
66 |
67 | @Override
68 | public void send(String text) {
69 | if (isOpen()) {
70 | super.send(text);
71 | client.getLogger().debug("▌ §c向服务端{}发送{}", getURI(), text);
72 | } else {
73 | client.getLogger().debug("▌ §c向服务端{}发送{}失败", getURI(), text);
74 | }
75 | }
76 |
77 | @Override
78 | public void reconnect() {
79 | reconnectTimes++;
80 | super.reconnect();
81 | if (reconnectTimes == client.getConfig().getReconnectMaxTimes() + 1) {
82 | client.getLogger().info("▌ §c连接至{}已达到最大次数", getURI());
83 | }
84 | }
85 |
86 | public void reconnectWebsocket() {
87 | TimerTask timerTask = new TimerTask() {
88 | @Override
89 | public void run() {
90 | reconnect();
91 | }
92 | };
93 | timer.schedule(timerTask, client.getConfig().getReconnectInterval() * 1000L);
94 | }
95 |
96 |
97 | public void stopWithoutReconnect(int code, String reason) {
98 | timer.cancel();
99 | close(code, reason);
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/cn/evole/onebot/client/core/Bot.java:
--------------------------------------------------------------------------------
1 | package cn.evole.onebot.client.core;
2 |
3 |
4 | import cn.evole.onebot.client.instances.action.ActionFactory;
5 | import cn.evole.onebot.sdk.action.BaseBot;
6 | import cn.evole.onebot.sdk.action.misc.ActionData;
7 | import cn.evole.onebot.sdk.action.misc.ActionList;
8 | import cn.evole.onebot.sdk.action.misc.ActionPath;
9 | import cn.evole.onebot.sdk.action.misc.ActionRaw;
10 | import cn.evole.onebot.sdk.entity.Anonymous;
11 | import cn.evole.onebot.sdk.entity.ArrayMsg;
12 | import cn.evole.onebot.sdk.entity.GuildMsgId;
13 | import cn.evole.onebot.sdk.entity.MsgId;
14 | import cn.evole.onebot.sdk.enums.ActionType;
15 | import cn.evole.onebot.sdk.event.message.WholeMessageEvent;
16 | import cn.evole.onebot.sdk.response.contact.FriendInfoResp;
17 | import cn.evole.onebot.sdk.response.contact.LoginInfoResp;
18 | import cn.evole.onebot.sdk.response.contact.StrangerInfoResp;
19 | import cn.evole.onebot.sdk.response.contact.UnidirectionalFriendListResp;
20 | import cn.evole.onebot.sdk.response.group.*;
21 | import cn.evole.onebot.sdk.response.guild.*;
22 | import cn.evole.onebot.sdk.response.misc.*;
23 | import cn.evole.onebot.sdk.util.GsonUtils;
24 | import com.google.gson.JsonArray;
25 | import com.google.gson.JsonObject;
26 | import com.google.gson.reflect.TypeToken;
27 | import lombok.Getter;
28 | import lombok.Setter;
29 | import lombok.val;
30 | import org.java_websocket.WebSocket;
31 |
32 | import java.util.List;
33 | import java.util.Map;
34 |
35 | /**
36 | * @Project: onebot-client
37 | * @Author: cnlimiter
38 | * @CreateTime: 2022/9/14 15:19
39 | * @Description:
40 | */
41 | @SuppressWarnings("unused")
42 | public class Bot implements BaseBot {
43 | private long selfId;
44 |
45 | private final ActionFactory actionFactory;
46 |
47 | @Getter
48 | @Setter
49 | private WebSocket channel;
50 |
51 | /**
52 | * @param channel {@link WebSocket}
53 | * @param actionFactory {@link ActionFactory}
54 | */
55 | public Bot(WebSocket channel, ActionFactory actionFactory) {
56 | this.channel = channel;
57 | this.actionFactory = actionFactory;
58 | }
59 |
60 | @Override
61 | public long getSelfId() {
62 | return this.selfId;
63 | }
64 |
65 | /**
66 | * 发送消息
67 | *
68 | * @param event {@link WholeMessageEvent}
69 | * @param msg 要发送的内容
70 | * @param autoEscape 消息内容是否作为纯文本发送 ( 即不解析 CQ 码 ) , 只在 message 字段是字符串时有效
71 | * @return {@link ActionData} of {@link MsgId}
72 | */
73 | public ActionData sendMsg(WholeMessageEvent event, String msg, boolean autoEscape) {
74 | if ("private".equals(event.getMessageType())) {
75 | return sendPrivateMsg(event.getUserId(), msg, autoEscape);
76 | }
77 | if ("group".equals(event.getMessageType())) {
78 | return sendGroupMsg(event.getGroupId(), msg, autoEscape);
79 | }
80 | return null;
81 | }
82 |
83 | /**
84 | * 发送消息
85 | *
86 | * @param event {@link WholeMessageEvent}
87 | * @param msg 消息链
88 | * @param autoEscape 消息内容是否作为纯文本发送 ( 即不解析 CQ 码 ) , 只在 message 字段是字符串时有效
89 | * @return result {@link ActionData} of {@link MsgId}
90 | */
91 | @Override
92 | public ActionData sendMsg(WholeMessageEvent event, List msg, boolean autoEscape) {
93 | if ("private".equals(event.getMessageType())) {
94 | return sendPrivateMsg(event.getUserId(), msg, autoEscape);
95 | }
96 | if ("group".equals(event.getMessageType())) {
97 | return sendGroupMsg(event.getGroupId(), msg, autoEscape);
98 | }
99 | return null;
100 | }
101 |
102 | /**
103 | * 发送私聊消息
104 | *
105 | * @param userId 对方 QQ 号
106 | * @param msg 要发送的内容(string)
107 | * @param autoEscape 消息内容是否作为纯文本发送 ( 即不解析 CQ 码 ) , 只在 message 字段是字符串时有效
108 | * @return {@link ActionData} of {@link MsgId}
109 | */
110 | public ActionData sendPrivateMsg(long userId, String msg, boolean autoEscape) {
111 | val action = ActionType.SEND_PRIVATE_MSG;
112 | val params = new JsonObject();
113 | params.addProperty("user_id", userId);
114 | params.addProperty("message", msg);
115 | params.addProperty("auto_escape", autoEscape);
116 | val result = actionFactory.action(channel, action, params);
117 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {}.getType()) : null;
118 | }
119 |
120 | /**
121 | * 发送私聊消息
122 | *
123 | * @param userId 对方 QQ 号
124 | * @param msg 消息链
125 | * @param autoEscape 消息内容是否作为纯文本发送 ( 即不解析 CQ 码 ) , 只在 message 字段是字符串时有效
126 | * @return result {@link ActionData} of {@link MsgId}
127 | */
128 | @Override
129 | public ActionData sendPrivateMsg(long userId, List msg, boolean autoEscape) {
130 | val action = ActionType.SEND_PRIVATE_MSG;
131 | val params = new JsonObject();
132 | params.addProperty("user_id", userId);
133 | params.addProperty("message", GsonUtils.getGson().toJson(msg));
134 | params.addProperty("auto_escape", autoEscape);
135 | val result = actionFactory.action(channel, action, params);
136 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {}.getType()) : null;
137 | }
138 |
139 | /**
140 | * 临时会话
141 | *
142 | * @param groupId 主动发起临时会话群号(机器人本身必须是管理员/群主)
143 | * @param userId 对方 QQ 号
144 | * @param msg 要发送的内容
145 | * @param autoEscape 消息内容是否作为纯文本发送 ( 即不解析 CQ 码 ) , 只在 message 字段是字符串时有效
146 | * @return result {@link ActionData} of {@link MsgId}
147 | */
148 | @Override
149 | public ActionData sendPrivateMsg(long groupId, long userId, String msg, boolean autoEscape) {
150 | val action = ActionType.SEND_PRIVATE_MSG;
151 | val params = new JsonObject();
152 | params.addProperty("group_id", groupId);
153 | params.addProperty("user_id", userId);
154 | params.addProperty("message", msg);
155 | params.addProperty("auto_escape", autoEscape);
156 | val result = actionFactory.action(channel, action, params);
157 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {}.getType()) : null;
158 | }
159 |
160 | /**
161 | * 临时会话
162 | *
163 | * @param groupId 主动发起临时会话群号(机器人本身必须是管理员/群主)
164 | * @param userId 对方 QQ 号
165 | * @param msg 消息链
166 | * @param autoEscape 消息内容是否作为纯文本发送 ( 即不解析 CQ 码 ) , 只在 message 字段是字符串时有效
167 | * @return result {@link ActionData} of {@link MsgId}
168 | */
169 | @Override
170 | public ActionData sendPrivateMsg(long groupId, long userId, List msg, boolean autoEscape) {
171 | val action = ActionType.SEND_PRIVATE_MSG;
172 | val params = new JsonObject();
173 | params.addProperty("group_id", groupId);
174 | params.addProperty("user_id", userId);
175 | params.addProperty("message", GsonUtils.getGson().toJson(msg));
176 | params.addProperty("auto_escape", autoEscape);
177 | val result = actionFactory.action(channel, action, params);
178 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {}.getType()) : null;
179 | }
180 |
181 | /**
182 | * 发送私聊消息
183 | *
184 | * @param userId 对方 QQ 号
185 | * @param msg 要发送的内容(array)
186 | * @param autoEscape 消息内容是否作为纯文本发送 ( 即不解析 CQ 码 ) , 只在 message 字段是字符串时有效
187 | * @return {@link ActionData} of {@link MsgId}
188 | */
189 | public ActionData sendPrivateMsg(long userId, JsonArray msg, boolean autoEscape) {
190 | val action = ActionType.SEND_PRIVATE_MSG;
191 | val params = new JsonObject();
192 | params.addProperty("user_id", userId);
193 | params.add("message", msg);
194 | params.addProperty("auto_escape", autoEscape);
195 | val result = actionFactory.action(channel, action, params);
196 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {}.getType()) : null;
197 | }
198 |
199 | /**
200 | * 发送群消息
201 | *
202 | * @param groupId 群号
203 | * @param msg 要发送的内容(string)
204 | * @param autoEscape 消息内容是否作为纯文本发送 ( 即不解析 CQ 码 ) , 只在 message 字段是字符串时有效
205 | * @return {@link ActionData} of {@link MsgId}
206 | */
207 | public ActionData sendGroupMsg(long groupId, String msg, boolean autoEscape) {
208 | val action = ActionType.SEND_GROUP_MSG;
209 | val params = new JsonObject();
210 | params.addProperty("group_id", groupId);
211 | params.addProperty("message", msg);
212 | params.addProperty("auto_escape", autoEscape);
213 | val result = actionFactory.action(channel, action, params);
214 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {}.getType()) : null;
215 | }
216 |
217 | @Override
218 | public ActionData sendGroupMsg(long groupId, List msg, boolean autoEscape) {
219 | val action = ActionType.SEND_GROUP_MSG;
220 | val params = new JsonObject();
221 | params.addProperty("group_id", groupId);
222 | params.addProperty("message", GsonUtils.getGson().toJson(msg));
223 | params.addProperty("auto_escape", autoEscape);
224 | val result = actionFactory.action(channel, action, params);
225 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {}.getType()) : null;
226 | }
227 |
228 | @Override
229 | public ActionData sendGroupMsg(long groupId, long userId, String msg, boolean autoEscape) {
230 | val action = ActionType.SEND_GROUP_MSG;
231 | val params = new JsonObject();
232 | params.addProperty("group_id", groupId);
233 | params.addProperty("user_id", userId);
234 | params.addProperty("message", msg);
235 | params.addProperty("auto_escape", autoEscape);
236 | val result = actionFactory.action(channel, action, params);
237 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {}.getType()) : null;
238 | }
239 |
240 | @Override
241 | public ActionData sendGroupMsg(long groupId, long userId, List msg, boolean autoEscape) {
242 | val action = ActionType.SEND_GROUP_MSG;
243 | val params = new JsonObject();
244 | params.addProperty("group_id", groupId);
245 | params.addProperty("user_id", userId);
246 | params.addProperty("message", GsonUtils.getGson().toJson(msg));
247 | params.addProperty("auto_escape", autoEscape);
248 | val result = actionFactory.action(channel, action, params);
249 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {}.getType()) : null;
250 | }
251 |
252 | /**
253 | * 发送群消息
254 | *
255 | * @param groupId 群号
256 | * @param msg 要发送的内容(array)
257 | * @param autoEscape 消息内容是否作为纯文本发送 ( 即不解析 CQ 码 ) , 只在 message 字段是字符串时有效
258 | * @return {@link ActionData} of {@link MsgId}
259 | */
260 | public ActionData sendGroupMsg(long groupId, JsonArray msg, boolean autoEscape) {
261 | val action = ActionType.SEND_GROUP_MSG;
262 | val params = new JsonObject();
263 | params.addProperty("group_id", groupId);
264 | params.add("message", msg);
265 | params.addProperty("auto_escape", autoEscape);
266 |
267 | val result = actionFactory.action(channel, action, params);
268 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {}.getType()) : null;
269 |
270 | }
271 |
272 | /**
273 | * 获取频道成员列表
274 | * 由于频道人数较多(数万), 请尽量不要全量拉取成员列表, 这将会导致严重的性能问题
275 | * 尽量使用 getGuildMemberProfile 接口代替全量拉取
276 | * nextToken 为空的情况下, 将返回第一页的数据, 并在返回值附带下一页的 token
277 | *
278 | * @param guildId 频道ID
279 | * @param nextToken 翻页Token
280 | * @return {@link ActionData} of {@link GuildMemberListResp}
281 | */
282 | public ActionData getGuildMemberList(String guildId, String nextToken) {
283 | val action = ActionType.GET_GUILD_LIST;
284 | val params = new JsonObject();
285 | params.addProperty("guild_id", guildId);
286 | params.addProperty("next_token", nextToken);
287 |
288 | val result = actionFactory.action(channel, action, params);
289 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
290 | }.getType()) : null;
291 | }
292 |
293 | /**
294 | * 发送信息到子频道
295 | *
296 | * @param guildId 频道 ID
297 | * @param channelId 子频道 ID
298 | * @param msg 要发送的内容(string)
299 | * @return {@link ActionData} of {@link GuildMsgId}
300 | */
301 | public ActionData sendGuildMsg(String guildId, String channelId, String msg) {
302 | val action = ActionType.SEND_GUILD_CHANNEL_MSG;
303 | val params = new JsonObject();
304 | params.addProperty("guild_id", guildId);
305 | params.addProperty("channel_id", channelId);
306 | params.addProperty("message", msg);
307 |
308 | val result = actionFactory.action(channel, action, params);
309 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
310 | }.getType()) : null;
311 | }
312 |
313 | /**
314 | * 发送信息到子频道
315 | *
316 | * @param guildId 频道 ID
317 | * @param channelId 子频道 ID
318 | * @param msg 要发送的内容(array)
319 | * @return {@link ActionData} of {@link GuildMsgId}
320 | */
321 | public ActionData sendGuildMsg(String guildId, String channelId, JsonArray msg) {
322 | val action = ActionType.SEND_GUILD_CHANNEL_MSG;
323 | val params = new JsonObject();
324 | params.addProperty("guild_id", guildId);
325 | params.addProperty("channel_id", channelId);
326 | params.add("message", msg);
327 |
328 | val result = actionFactory.action(channel, action, params);
329 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
330 | }.getType()) : null;
331 | }
332 |
333 | /**
334 | * 获取频道消息
335 | *
336 | * @param guildMsgId 频道 ID
337 | * @param noCache 是否使用缓存
338 | * @return {@link ActionData} of {@link GetGuildMsgResp}
339 | */
340 | public ActionData getGuildMsg(String guildMsgId, boolean noCache) {
341 | val action = ActionType.GET_GUILD_MSG;
342 | val params = new JsonObject();
343 | params.addProperty("message_id", guildMsgId);
344 | params.addProperty("no_cache", noCache);
345 |
346 | val result = actionFactory.action(channel, action, params);
347 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
348 | }.getType()) : null;
349 | }
350 |
351 | /**
352 | * 获取频道系统内 BOT 的资料
353 | *
354 | * @return {@link ActionData} of {@link GuildServiceProfileResp}
355 | */
356 | public ActionData getGuildServiceProfile() {
357 | val action = ActionType.GET_GUILD_SERVICE_PROFILE;
358 | val result = actionFactory.action(channel, action, null);
359 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
360 | }.getType()) : null;
361 | }
362 |
363 | /**
364 | * 获取频道列表
365 | *
366 | * @return {@link ActionList} of {@link GuildListResp}
367 | */
368 | public ActionList getGuildList() {
369 | val action = ActionType.GET_GUILD_LIST;
370 | val result = actionFactory.action(channel, action, null);
371 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
372 | }.getType()) : null;
373 | }
374 |
375 | /**
376 | * 通过访客获取频道元数据
377 | *
378 | * @param guildId 频道 ID
379 | * @return {@link ActionData} of {@link GuildMetaByGuestResp}
380 | */
381 | public ActionData getGuildMetaByGuest(String guildId) {
382 | val action = ActionType.GET_GUILD_META_BY_GUEST;
383 | val params = new JsonObject();
384 | params.addProperty("guild_id", guildId);
385 |
386 | val result = actionFactory.action(channel, action, params);
387 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
388 | }.getType()) : null;
389 | }
390 |
391 | /**
392 | * 获取子频道列表
393 | *
394 | * @param guildId 频道 ID
395 | * @param noCache 是否无视缓存
396 | * @return {@link ActionList} of {@link ChannelInfoResp}
397 | */
398 | public ActionList getGuildChannelList(String guildId, boolean noCache) {
399 | val action = ActionType.GET_GUILD_CHANNEL_LIST;
400 | val params = new JsonObject();
401 | params.addProperty("guild_id", guildId);
402 | params.addProperty("no_cache", noCache);
403 |
404 | val result = actionFactory.action(channel, action, params);
405 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
406 | }.getType()) : null;
407 | }
408 |
409 | /**
410 | * 单独获取频道成员信息
411 | *
412 | * @param guildId 频道ID
413 | * @param userId 用户ID
414 | * @return {@link ActionData} of {@link GuildMemberProfileResp}
415 | */
416 | public ActionData getGuildMemberProfile(String guildId, String userId) {
417 | val action = ActionType.GET_GUILD_MEMBER_PROFILE;
418 | val params = new JsonObject();
419 | params.addProperty("guild_id", guildId);
420 | params.addProperty("user_id", userId);
421 |
422 | val result = actionFactory.action(channel, action, params);
423 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
424 | }.getType()) : null;
425 | }
426 |
427 | /**
428 | * 获取消息
429 | *
430 | * @param msgId 消息 ID
431 | * @return {@link ActionData} of {@link GetMsgResp}
432 | */
433 | public ActionData getMsg(int msgId) {
434 | val action = ActionType.GET_MSG;
435 | val params = new JsonObject();
436 | params.addProperty("message_id", msgId);
437 |
438 | val result = actionFactory.action(channel, action, params);
439 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
440 | }.getType()) : null;
441 | }
442 |
443 | /**
444 | * 撤回消息
445 | *
446 | * @param msgId 消息 ID
447 | * @return {@link ActionRaw}
448 | */
449 | public ActionRaw deleteMsg(int msgId) {
450 | val action = ActionType.DELETE_MSG;
451 | val params = new JsonObject();
452 | params.addProperty("message_id", msgId);
453 |
454 | val result = actionFactory.action(channel, action, params);
455 | return result != null ? GsonUtils.fromJson(result.toString(), ActionRaw.class) : null;
456 | }
457 |
458 | @Override
459 | public ActionRaw deleteMsg(long groupId, long userId, int msgId) {
460 | val action = ActionType.DELETE_MSG;
461 | val params = new JsonObject();
462 | params.addProperty("message_id", msgId);
463 | params.addProperty("user_id", userId);
464 | params.addProperty("group_id", groupId);
465 | val result = actionFactory.action(channel, action, params);
466 | return result != null ? GsonUtils.fromJson(result.toString(), ActionRaw.class) : null;
467 | }
468 |
469 | /**
470 | * 群组踢人
471 | *
472 | * @param groupId 群号
473 | * @param userId 要踢的 QQ 号
474 | * @param rejectAddRequest 拒绝此人的加群请求 (默认false)
475 | * @return {@link ActionRaw}
476 | */
477 | public ActionRaw setGroupKick(long groupId, long userId, boolean rejectAddRequest) {
478 | val action = ActionType.SET_GROUP_KICK;
479 | val params = new JsonObject();
480 | params.addProperty("group_id", groupId);
481 | params.addProperty("user_id", userId);
482 | params.addProperty("reject_add_request", rejectAddRequest);
483 |
484 | val result = actionFactory.action(channel, action, params);
485 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
486 | }
487 |
488 | /**
489 | * 群组单人禁言
490 | *
491 | * @param groupId 群号
492 | * @param userId 要禁言的 QQ 号
493 | * @param duration 禁言时长, 单位秒, 0 表示取消禁言 (默认30 * 60)
494 | * @return {@link ActionRaw}
495 | */
496 | public ActionRaw setGroupBan(long groupId, long userId, int duration) {
497 | val action = ActionType.SET_GROUP_BAN;
498 | val params = new JsonObject();
499 | params.addProperty("group_id", groupId);
500 | params.addProperty("user_id", userId);
501 | params.addProperty("duration", duration);
502 |
503 | val result = actionFactory.action(channel, action, params);
504 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
505 | }
506 |
507 | /**
508 | * 全体禁言
509 | *
510 | * @param groupId 群号
511 | * @param enable 是否禁言(默认True,False为取消禁言)
512 | * @return {@link ActionRaw}
513 | */
514 | public ActionRaw setGroupWholeBan(long groupId, boolean enable) {
515 | val action = ActionType.SET_GROUP_WHOLE_BAN;
516 | val params = new JsonObject();
517 | params.addProperty("group_id", groupId);
518 | params.addProperty("enable", enable);
519 |
520 | val result = actionFactory.action(channel, action, params);
521 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
522 | }
523 |
524 | /**
525 | * 群组设置管理员
526 | *
527 | * @param groupId 群号
528 | * @param userId 要设置管理员的 QQ 号
529 | * @param enable true 为设置,false 为取消
530 | * @return {@link ActionRaw}
531 | */
532 | public ActionRaw setGroupAdmin(long groupId, long userId, boolean enable) {
533 | val action = ActionType.SET_GROUP_ADMIN;
534 | val params = new JsonObject();
535 | params.addProperty("group_id", groupId);
536 | params.addProperty("user_id", userId);
537 | params.addProperty("enable", enable);
538 |
539 | val result = actionFactory.action(channel, action, params);
540 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
541 | }
542 |
543 | /**
544 | * 群组匿名
545 | *
546 | * @param groupId 群号
547 | * @param enable 是否允许匿名聊天
548 | * @return {@link ActionRaw}
549 | */
550 | public ActionRaw setGroupAnonymous(long groupId, boolean enable) {
551 | val action = ActionType.SET_GROUP_ANONYMOUS;
552 | val params = new JsonObject();
553 | params.addProperty("group_id", groupId);
554 | params.addProperty("enable", enable);
555 |
556 | val result = actionFactory.action(channel, action, params);
557 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
558 | }
559 |
560 | /**
561 | * 设置群名片(群备注)
562 | *
563 | * @param groupId 群号
564 | * @param userId 要设置的 QQ 号
565 | * @param card 群名片内容,不填或空字符串表示删除群名片
566 | * @return {@link ActionRaw}
567 | */
568 | public ActionRaw setGroupCard(long groupId, long userId, String card) {
569 | val action = ActionType.SET_GROUP_CARD;
570 | val params = new JsonObject();
571 | params.addProperty("group_id", groupId);
572 | params.addProperty("user_id", userId);
573 | params.addProperty("card", card);
574 |
575 | val result = actionFactory.action(channel, action, params);
576 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
577 | }
578 |
579 | /**
580 | * 设置群名
581 | *
582 | * @param groupId 群号
583 | * @param groupName 新群名
584 | * @return {@link ActionRaw}
585 | */
586 | public ActionRaw setGroupName(long groupId, String groupName) {
587 | val action = ActionType.SET_GROUP_NAME;
588 | val params = new JsonObject();
589 | params.addProperty("group_id", groupId);
590 | params.addProperty("group_name", groupName);
591 |
592 | val result = actionFactory.action(channel, action, params);
593 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
594 | }
595 |
596 | /**
597 | * 退出群组
598 | *
599 | * @param groupId 群号
600 | * @param isDismiss 是否解散, 如果登录号是群主, 则仅在此项为 true 时能够解散
601 | * @return {@link ActionRaw}
602 | */
603 | public ActionRaw setGroupLeave(long groupId, boolean isDismiss) {
604 | val action = ActionType.SET_GROUP_LEAVE;
605 | val params = new JsonObject();
606 | params.addProperty("group_id", groupId);
607 | params.addProperty("is_dismiss", isDismiss);
608 |
609 | val result = actionFactory.action(channel, action, params);
610 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
611 | }
612 |
613 | /**
614 | * 设置群组专属头衔
615 | *
616 | * @param groupId 群号
617 | * @param userId 要设置的 QQ 号
618 | * @param specialTitle 专属头衔,不填或空字符串表示删除专属头衔
619 | * @param duration 专属头衔有效期,单位秒,-1 表示永久,不过此项似乎没有效果,可能是只有某些特殊的时间长度有效,有待测试
620 | * @return {@link ActionRaw}
621 | */
622 | public ActionRaw setGroupSpecialTitle(long groupId, long userId, String specialTitle, int duration) {
623 | val action = ActionType.SET_GROUP_SPECIAL_TITLE;
624 | val params = new JsonObject();
625 | params.addProperty("group_id", groupId);
626 | params.addProperty("user_id", userId);
627 | params.addProperty("special_title", specialTitle);
628 | params.addProperty("duration", duration);
629 |
630 | val result = actionFactory.action(channel, action, params);
631 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
632 | }
633 |
634 | /**
635 | * 处理加好友请求
636 | *
637 | * @param flag 加好友请求的 flag(需从上报的数据中获得)
638 | * @param approve 是否同意请求(默认为true)
639 | * @param remark 添加后的好友备注(仅在同意时有效)
640 | * @return {@link ActionRaw}
641 | */
642 | public ActionRaw setFriendAddRequest(String flag, boolean approve, String remark) {
643 | val action = ActionType.SET_FRIEND_ADD_REQUEST;
644 | val params = new JsonObject();
645 | params.addProperty("flag", flag);
646 | params.addProperty("approve", approve);
647 | params.addProperty("remark", remark);
648 |
649 | val result = actionFactory.action(channel, action, params);
650 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
651 | }
652 |
653 | /**
654 | * 处理加群请求/邀请
655 | *
656 | * @param flag 加群请求的 flag(需从上报的数据中获得)
657 | * @param subType add 或 invite,请求类型(需要和上报消息中的 sub_type 字段相符)
658 | * @param approve 是否同意请求/邀请
659 | * @param reason 拒绝理由(仅在拒绝时有效)
660 | * @return {@link ActionRaw}
661 | */
662 | public ActionRaw setGroupAddRequest(String flag, String subType, boolean approve, String reason) {
663 | val action = ActionType.SET_GROUP_ADD_REQUEST;
664 | val params = new JsonObject();
665 | params.addProperty("flag", flag);
666 | params.addProperty("sub_type", subType);
667 | params.addProperty("approve", approve);
668 | params.addProperty("reason", reason);
669 |
670 | val result = actionFactory.action(channel, action, params);
671 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
672 | }
673 |
674 | /**
675 | * 获取登录号信息
676 | *
677 | * @return {@link ActionData} of @{@link LoginInfoResp}
678 | */
679 | public ActionData getLoginInfo() {
680 | val action = ActionType.GET_LOGIN_INFO;
681 | val result = actionFactory.action(channel, action, null);
682 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
683 | }.getType()) : null;
684 | }
685 |
686 | /**
687 | * 获取陌生人信息
688 | *
689 | * @param userId QQ 号
690 | * @param noCache 是否不使用缓存(使用缓存可能更新不及时,但响应更快)
691 | * @return {@link ActionData} of {@link StrangerInfoResp}
692 | */
693 | public ActionData getStrangerInfo(long userId, boolean noCache) {
694 | val action = ActionType.GET_STRANGER_INFO;
695 | val params = new JsonObject();
696 | params.addProperty("user_id", userId);
697 | params.addProperty("no_cache", noCache);
698 |
699 | val result = actionFactory.action(channel, action, params);
700 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
701 | }.getType()) : null;
702 | }
703 |
704 | /**
705 | * 获取好友列表
706 | *
707 | * @return {@link ActionList} of {@link FriendInfoResp}
708 | */
709 | public ActionList getFriendList() {
710 | val action = ActionType.GET_FRIEND_LIST;
711 | val result = actionFactory.action(channel, action, null);
712 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
713 | }.getType()) : null;
714 | }
715 |
716 | /**
717 | * 删除好友
718 | *
719 | * @param friendId 好友 QQ 号
720 | * @return {@link ActionRaw}
721 | */
722 | public ActionRaw deleteFriend(long friendId) {
723 | val action = ActionType.DELETE_FRIEND;
724 | val params = new JsonObject();
725 | params.addProperty("friend_id", friendId);
726 |
727 | val result = actionFactory.action(channel, action, params);
728 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
729 | }
730 |
731 | /**
732 | * 获取群信息
733 | *
734 | * @param groupId 群号
735 | * @param noCache 是否不使用缓存(使用缓存可能更新不及时,但响应更快)
736 | * @return {@link ActionData} of {@link GroupInfoResp}
737 | */
738 | public ActionData getGroupInfo(long groupId, boolean noCache) {
739 | val action = ActionType.GET_GROUP_INFO;
740 | val params = new JsonObject();
741 | params.addProperty("group_id", groupId);
742 | params.addProperty("no_cache", noCache);
743 |
744 | val result = actionFactory.action(channel, action, params);
745 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
746 | }.getType()) : null;
747 | }
748 |
749 | /**
750 | * 获取群列表
751 | *
752 | * @return {@link ActionList} of {@link GroupInfoResp}
753 | */
754 | public ActionList getGroupList() {
755 | val action = ActionType.GET_GROUP_LIST;
756 | val result = actionFactory.action(channel, action, null);
757 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
758 | }.getType()) : null;
759 | }
760 |
761 | /**
762 | * 获取群成员信息
763 | *
764 | * @param groupId 群号
765 | * @param userId QQ 号
766 | * @param noCache 是否不使用缓存(使用缓存可能更新不及时,但响应更快)
767 | * @return {@link ActionData} of {@link GroupMemberInfoResp}
768 | */
769 | public ActionData getGroupMemberInfo(long groupId, long userId, boolean noCache) {
770 | val action = ActionType.GET_GROUP_MEMBER_INFO;
771 | val params = new JsonObject();
772 | params.addProperty("group_id", groupId);
773 | params.addProperty("user_id", userId);
774 | params.addProperty("no_cache", noCache);
775 |
776 | val result = actionFactory.action(channel, action, params);
777 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
778 | }.getType()) : null;
779 | }
780 |
781 | /**
782 | * 获取群成员列表
783 | *
784 | * @param groupId 群号
785 | * @return {@link ActionList} of {@link GroupMemberInfoResp}
786 | */
787 | public ActionList getGroupMemberList(long groupId) {
788 | val action = ActionType.GET_GROUP_MEMBER_LIST;
789 | val params = new JsonObject();
790 | params.addProperty("group_id", groupId);
791 |
792 | val result = actionFactory.action(channel, action, params);
793 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
794 | }.getType()) : null;
795 | }
796 |
797 | @Override
798 | public ActionList getGroupMemberList(long groupId, boolean noCache) {
799 | val action = ActionType.GET_GROUP_MEMBER_LIST;
800 | val params = new JsonObject();
801 | params.addProperty("group_id", groupId);
802 | params.addProperty("no_cache", noCache);
803 | val result = actionFactory.action(channel, action, params);
804 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
805 | }.getType()) : null;
806 | }
807 |
808 | /**
809 | * 获取群荣誉信息
810 | *
811 | * @param groupId 群号
812 | * @param type 要获取的群荣誉类型, 可传入 talkative performer legend strong_newbie emotion 以分别获取单个类型的群荣誉数据, 或传入 all 获取所有数据
813 | * @return {@link ActionData} of {@link GroupHonorInfoResp}
814 | */
815 | public ActionData getGroupHonorInfo(long groupId, String type) {
816 | val action = ActionType.GET_GROUP_HONOR_INFO;
817 | val params = new JsonObject();
818 | params.addProperty("group_id", groupId);
819 | params.addProperty("type", type);
820 |
821 | val result = actionFactory.action(channel, action, params);
822 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
823 | }.getType()) : null;
824 | }
825 |
826 | /**
827 | * 检查是否可以发送图片
828 | *
829 | * @return {@link ActionData} of {@link BooleanResp}
830 | */
831 | public ActionData canSendImage() {
832 | val action = ActionType.CAN_SEND_IMAGE;
833 | val result = actionFactory.action(channel, action, null);
834 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
835 | }.getType()) : null;
836 | }
837 |
838 | /**
839 | * 检查是否可以发送语音
840 | *
841 | * @return {@link ActionData} of {@link BooleanResp}
842 | */
843 | public ActionData canSendRecord() {
844 | val action = ActionType.CAN_SEND_RECORD;
845 | val result = actionFactory.action(channel, action, null);
846 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
847 | }.getType()) : null;
848 | }
849 |
850 | /**
851 | * 设置群头像
852 | * 目前这个API在登录一段时间后因cookie失效而失效, 请考虑后使用
853 | *
854 | * @param groupId 群号
855 | * @param file 图片文件名(支持绝对路径,网络URL,Base64编码)
856 | * @param cache 表示是否使用已缓存的文件 (通过网络URL发送时有效, 1表示使用缓存, 0关闭关闭缓存, 默认为1)
857 | * @return {@link ActionRaw}
858 | */
859 | public ActionRaw setGroupPortrait(long groupId, String file, int cache) {
860 | val action = ActionType.SET_GROUP_PORTRAIT;
861 | val params = new JsonObject();
862 | params.addProperty("group_id", groupId);
863 | params.addProperty("file", file);
864 | params.addProperty("cache", cache);
865 |
866 | val result = actionFactory.action(channel, action, params);
867 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
868 | }
869 |
870 | /**
871 | * 检查链接安全性
872 | * 安全等级, 1: 安全 2: 未知 3: 危险
873 | *
874 | * @param url 需要检查的链接
875 | * @return {@link ActionData} of {@link CheckUrlSafelyResp}
876 | */
877 | public ActionData checkUrlSafely(String url) {
878 | val action = ActionType.CHECK_URL_SAFELY;
879 | val params = new JsonObject();
880 | params.addProperty("url", url);
881 |
882 | val result = actionFactory.action(channel, action, params);
883 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
884 | }.getType()) : null;
885 | }
886 |
887 | /**
888 | * 发送群公告
889 | *
890 | * @param groupId 群号
891 | * @param content 公告内容
892 | * @return {@link ActionRaw}
893 | */
894 | public ActionRaw sendGroupNotice(long groupId, String content) {
895 | val action = ActionType.SEN_GROUP_NOTICE;
896 | val params = new JsonObject();
897 | params.addProperty("group_id", groupId);
898 | params.addProperty("content", content);
899 |
900 | val result = actionFactory.action(channel, action, params);
901 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
902 | }
903 |
904 | /**
905 | * 获取群 @全体成员 剩余次数
906 | *
907 | * @param groupId 群号
908 | * @return {@link ActionData} of {@link GroupAtAllRemainResp}
909 | */
910 | public ActionData getGroupAtAllRemain(long groupId) {
911 | val action = ActionType.GET_GROUP_AT_ALL_REMAIN;
912 | val params = new JsonObject();
913 | params.addProperty("group_id", groupId);
914 |
915 | val result = actionFactory.action(channel, action, params);
916 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
917 | }.getType()) : null;
918 | }
919 |
920 | /**
921 | * 上传群文件
922 | * 在不提供 folder 参数的情况下默认上传到根目录
923 | * 只能上传本地文件, 需要上传 http 文件的话请先下载到本地
924 | *
925 | * @param groupId 群号
926 | * @param file 本地文件路径
927 | * @param name 储存名称
928 | * @param folder 父目录ID
929 | * @return {@link ActionRaw}
930 | */
931 | public ActionRaw uploadGroupFile(long groupId, String file, String name, String folder) {
932 | val action = ActionType.UPLOAD_GROUP_FILE;
933 | val params = new JsonObject();
934 | params.addProperty("group_id", groupId);
935 | params.addProperty("file", file);
936 | params.addProperty("name", name);
937 | params.addProperty("folder", folder);
938 |
939 | val result = actionFactory.action(channel, action, params);
940 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
941 | }
942 |
943 | /**
944 | * 上传群文件
945 | * 在不提供 folder 参数的情况下默认上传到根目录
946 | * 只能上传本地文件, 需要上传 http 文件的话请先下载到本地
947 | *
948 | * @param groupId 群号
949 | * @param file 本地文件路径
950 | * @param name 储存名称
951 | * @return {@link ActionRaw}
952 | */
953 | public ActionRaw uploadGroupFile(long groupId, String file, String name) {
954 | val action = ActionType.UPLOAD_GROUP_FILE;
955 | val params = new JsonObject();
956 | params.addProperty("group_id", groupId);
957 | params.addProperty("file", file);
958 | params.addProperty("name", name);
959 |
960 | val result = actionFactory.action(channel, action, params);
961 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
962 | }
963 |
964 | /**
965 | * 群组匿名用户禁言
966 | *
967 | * @param groupId 群号
968 | * @param anonymous 要禁言的匿名用户对象(群消息上报的 anonymous 字段)
969 | * @param duration 禁言时长,单位秒,无法取消匿名用户禁言
970 | * @return {@link ActionRaw}
971 | */
972 | public ActionRaw setGroupAnonymousBan(long groupId, Anonymous anonymous, int duration) {
973 | val action = ActionType.SET_GROUP_ANONYMOUS_BAN;
974 | String an = GsonUtils.getGson().toJson(anonymous);
975 | val params = new JsonObject();
976 | params.addProperty("group_id", groupId);
977 | params.add("anonymous", GsonUtils.parse(an));
978 | params.addProperty("duration", duration);
979 |
980 | val result = actionFactory.action(channel, action, params);
981 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
982 | }
983 |
984 |
985 | /**
986 | * 群组匿名用户禁言
987 | *
988 | * @param groupId 群号
989 | * @param flag 要禁言的匿名用户的 flag(需从群消息上报的数据中获得)
990 | * @param duration 禁言时长,单位秒,无法取消匿名用户禁言
991 | * @return {@link ActionRaw}
992 | */
993 | public ActionRaw setGroupAnonymousBan(long groupId, String flag, int duration) {
994 | val action = ActionType.SET_GROUP_ANONYMOUS_BAN;
995 | val params = new JsonObject();
996 | params.addProperty("group_id", groupId);
997 | params.addProperty("flag", flag);
998 | params.addProperty("duration", duration);
999 |
1000 | val result = actionFactory.action(channel, action, params);
1001 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
1002 | }
1003 |
1004 | /**
1005 | * 调用 go cq http 下载文件
1006 | *
1007 | * @param url 链接地址
1008 | * @param threadCount 下载线程数
1009 | * @param headers 自定义请求头
1010 | * @return {@link ActionData} of {@link DownloadFileResp}
1011 | */
1012 | public ActionData downloadFile(String url, int threadCount, String headers) {
1013 | val action = ActionType.DOWNLOAD_FILE;
1014 | val params = new JsonObject();
1015 | params.addProperty("url", url);
1016 | params.addProperty("thread_count", threadCount);
1017 | params.addProperty("headers", headers);
1018 |
1019 | val result = actionFactory.action(channel, action, params);
1020 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
1021 | }.getType()) : null;
1022 | }
1023 |
1024 | /**
1025 | * 调用 go cq http 下载文件
1026 | *
1027 | * @param url 链接地址
1028 | * @return {@link ActionData} of {@link DownloadFileResp}
1029 | */
1030 | public ActionData downloadFile(String url) {
1031 | val action = ActionType.DOWNLOAD_FILE;
1032 | val params = new JsonObject();
1033 | params.addProperty("url", url);
1034 |
1035 | val result = actionFactory.action(channel, action, params);
1036 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
1037 | }.getType()) : null;
1038 |
1039 | }
1040 |
1041 |
1042 |
1043 | /**
1044 | * 获取群根目录文件列表
1045 | *
1046 | * @param groupId 群号
1047 | * @return {@link ActionData} of {@link GroupFilesResp}
1048 | */
1049 | public ActionData getGroupRootFiles(long groupId) {
1050 | val action = ActionType.GET_GROUP_ROOT_FILES;
1051 | val params = new JsonObject();
1052 | params.addProperty("group_id", groupId);
1053 |
1054 | val result = actionFactory.action(channel, action, params);
1055 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
1056 | }.getType()) : null;
1057 | }
1058 |
1059 | /**
1060 | * 获取群子目录文件列表
1061 | *
1062 | * @param groupId 群号
1063 | * @param folderId 文件夹ID 参考 Folder 对象
1064 | * @return {@link ActionData} of {@link GroupFilesResp}
1065 | */
1066 | public ActionData getGroupFilesByFolder(long groupId, String folderId) {
1067 | val action = ActionType.GET_GROUP_FILES_BY_FOLDER;
1068 | val params = new JsonObject();
1069 | params.addProperty("group_id", groupId);
1070 | params.addProperty("folder_id", folderId);
1071 |
1072 | val result = actionFactory.action(channel, action, params);
1073 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
1074 | }.getType()) : null;
1075 | }
1076 |
1077 | /**
1078 | * 自定义请求
1079 | *
1080 | * @param action 请求路径
1081 | * @param params 请求参数
1082 | * @return {@link ActionData}
1083 | */
1084 | @SuppressWarnings("rawtypes")
1085 | public ActionData customRequest(ActionPath action, JsonObject params) {
1086 | val result = actionFactory.action(channel, action, params);
1087 | return result != null ? GsonUtils.fromJson(result.toString(),ActionData.class) : null;
1088 | }
1089 |
1090 | /**
1091 | * 获取精华消息列表
1092 | *
1093 | * @param groupId 群号
1094 | * @return {@link ActionList} of {@link EssenceMsgResp}
1095 | */
1096 | public ActionList getEssenceMsgList(long groupId) {
1097 | val action = ActionType.GET_ESSENCE_MSG_LIST;
1098 | val params = new JsonObject();
1099 | params.addProperty("group_id", groupId);
1100 |
1101 | val result = actionFactory.action(channel, action, params);
1102 | return result != null ? GsonUtils.fromJson(result.toString(), new TypeToken>() {
1103 | }.getType()) : null;
1104 | }
1105 |
1106 | /**
1107 | * 设置精华消息
1108 | *
1109 | * @param msgId 消息 ID
1110 | * @return {@link ActionRaw}
1111 | */
1112 | public ActionRaw setEssenceMsg(int msgId) {
1113 | val action = ActionType.SET_ESSENCE_MSG;
1114 | val params = new JsonObject();
1115 | params.addProperty("message_id", msgId);
1116 |
1117 | val result = actionFactory.action(channel, action, params);
1118 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
1119 | }
1120 |
1121 | /**
1122 | * 移出精华消息
1123 | *
1124 | * @param msgId 消息 ID
1125 | * @return {@link ActionRaw}
1126 | */
1127 | public ActionRaw deleteEssenceMsg(int msgId) {
1128 | val action = ActionType.DELETE_ESSENCE_MSG;
1129 | val params = new JsonObject();
1130 | params.addProperty("message_id", msgId);
1131 |
1132 | val result = actionFactory.action(channel, action, params);
1133 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
1134 | }
1135 |
1136 | /**
1137 | * 设置机器人账号资料
1138 | *
1139 | * @param nickname 昵称
1140 | * @param company 公司
1141 | * @param email 邮箱
1142 | * @param college 学校
1143 | * @param personalNote 个性签名
1144 | * @return {@link ActionRaw}
1145 | */
1146 | public ActionRaw setBotProfile(String nickname, String company, String email, String college, String personalNote) {
1147 | val action = ActionType.SET_QQ_PROFILE;
1148 | val params = new JsonObject();
1149 | params.addProperty("nickname", nickname);
1150 | params.addProperty("company", company);
1151 | params.addProperty("email", email);
1152 | params.addProperty("college", college);
1153 | params.addProperty("personalNote", personalNote);
1154 |
1155 | val result = actionFactory.action(channel, action, params);
1156 | return result != null ? GsonUtils.fromJson(result.toString(),ActionRaw.class) : null;
1157 | }
1158 |
1159 |
1160 | /**
1161 | * 发送合并转发 (群)
1162 | *
1163 | * @param groupId 群号
1164 | * @param msg 自定义转发消息 (可使用 BotUtils.generateForwardMsg() 方法创建)
1165 | * 参考文档
1166 | * @return {@link ActionRaw}
1167 | */
1168 | public ActionData sendGroupForwardMsg(long groupId, List